mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-25 09:29:22 +00:00
Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
119
.gitlab-ci.yml
Normal file
119
.gitlab-ci.yml
Normal file
@@ -0,0 +1,119 @@
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
PYTHON: "C:\\python38\\python.exe"
|
||||
NSIS: "C:\\program files (x86)\\nsis\\makensis.exe"
|
||||
|
||||
stages:
|
||||
- build
|
||||
- make_installer
|
||||
- upload
|
||||
|
||||
snapshot32:
|
||||
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.7 -y -ForceX86
|
||||
- '&$env:PYTHON -V'
|
||||
- '&$env:PYTHON -m pip install --upgrade pip'
|
||||
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt'
|
||||
- '&$env:PYTHON -m pip uninstall enum34 -y'
|
||||
stage: build
|
||||
interruptible: true
|
||||
script:
|
||||
# Create html documentation firstly.
|
||||
- cd doc
|
||||
- '&$env:PYTHON documentation_importer.py'
|
||||
- cd ..\src
|
||||
- '&$env:PYTHON ..\doc\generator.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_snapshot_x86.zip
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
paths:
|
||||
- artifacts
|
||||
expire_in: 1 day
|
||||
|
||||
snapshot64:
|
||||
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.7 -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 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_snapshot_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/
|
||||
- cd scripts
|
||||
- '&$env:NSIS twblue_snapshot.nsi'
|
||||
- move twblue_snapshot_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
|
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:\\Python38"
|
||||
PYTHON_VERSION: "3.8.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'
|
@@ -2,6 +2,8 @@
|
||||
|
||||
## changes in this version
|
||||
|
||||
* 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%.
|
||||
|
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,7 +14,7 @@ SetCompress auto
|
||||
SetCompressor /solid lzma
|
||||
SetDatablockOptimize on
|
||||
VIAddVersionKey ProductName "TWBlue"
|
||||
VIAddVersionKey LegalCopyright "Copyright 2018 Manuel Cortéz."
|
||||
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
|
||||
VIAddVersionKey ProductVersion "0.95"
|
||||
VIAddVersionKey FileVersion "0.95"
|
||||
VIProductVersion "0.95.0.0"
|
||||
@@ -27,7 +27,7 @@ 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
|
||||
|
95
scripts/twblue_snapshot.nsi
Normal file
95
scripts/twblue_snapshot.nsi
Normal file
@@ -0,0 +1,95 @@
|
||||
!include "MUI2.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
!include "x64.nsh"
|
||||
Unicode true
|
||||
CRCCheck on
|
||||
ManifestSupportedOS all
|
||||
XPStyle on
|
||||
Name "TWBlue"
|
||||
OutFile "TWBlue_snapshot_setup.exe"
|
||||
InstallDir "$PROGRAMFILES\twblue"
|
||||
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
|
||||
RequestExecutionLevel admin
|
||||
SetCompress auto
|
||||
SetCompressor /solid lzma
|
||||
SetDatablockOptimize on
|
||||
VIAddVersionKey ProductName "TWBlue Snapshot version"
|
||||
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
|
||||
VIAddVersionKey ProductVersion "8"
|
||||
VIAddVersionKey FileVersion "8"
|
||||
VIProductVersion "8.0.0.0"
|
||||
VIFileVersion "8.0.0.0"
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!define MUI_LICENSEPAGE_RADIOBUTTONS
|
||||
!insertmacro MUI_PAGE_LICENSE "license.txt"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
var StartMenuFolder
|
||||
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
|
||||
!define MUI_FINISHPAGE_LINK_LOCATION "https://twblue.es"
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
!insertmacro MUI_LANGUAGE "French"
|
||||
!insertmacro MUI_LANGUAGE "Spanish"
|
||||
!insertmacro MUI_LANGUAGE "Italian"
|
||||
!insertmacro MUI_LANGUAGE "Finnish"
|
||||
!insertmacro MUI_LANGUAGE "Russian"
|
||||
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
||||
!insertmacro MUI_LANGUAGE "Polish"
|
||||
!insertmacro MUI_LANGUAGE "German"
|
||||
!insertmacro MUI_LANGUAGE "Hungarian"
|
||||
!insertmacro MUI_LANGUAGE "Turkish"
|
||||
!insertmacro MUI_LANGUAGE "Arabic"
|
||||
!insertmacro MUI_LANGUAGE "Galician"
|
||||
!insertmacro MUI_LANGUAGE "Catalan"
|
||||
!insertmacro MUI_LANGUAGE "Basque"
|
||||
!insertmacro MUI_LANGUAGE "Croatian"
|
||||
!insertmacro MUI_LANGUAGE "Japanese"
|
||||
!insertmacro MUI_LANGUAGE "SerbianLatin"
|
||||
!insertmacro MUI_LANGUAGE "Romanian"
|
||||
!insertmacro MUI_RESERVEFILE_LANGDLL
|
||||
Section
|
||||
SetShellVarContext All
|
||||
SetOutPath "$INSTDIR"
|
||||
${If} ${RunningX64}
|
||||
File /r TWBlue64\*
|
||||
${Else}
|
||||
File /r TWBlue\*
|
||||
${EndIf}
|
||||
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
|
||||
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TWBlue"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "8"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "https://twblue.es"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
|
||||
SectionEnd
|
||||
Section "Uninstall"
|
||||
SetShellVarContext All
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
|
||||
RMDir /r /REBOOTOK $INSTDIR
|
||||
Delete "$DESKTOP\TWBlue.lnk"
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
|
||||
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
|
||||
SectionEnd
|
||||
Function .onInit
|
||||
${If} ${RunningX64}
|
||||
StrCpy $instdir "$programfiles64\twblue"
|
||||
${EndIf}
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
FunctionEnd
|
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()
|
@@ -9,7 +9,7 @@ if snapshot == False:
|
||||
update_url = 'https://twblue.es/updates/stable.php'
|
||||
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
|
||||
else:
|
||||
version = "6"
|
||||
version = "8"
|
||||
update_url = 'https://twblue.es/updates/snapshot.php'
|
||||
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
|
||||
authors = ["Manuel Cortéz", "José Manuel Delicado"]
|
||||
|
@@ -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()
|
@@ -1,26 +1,14 @@
|
||||
# -*- 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")
|
||||
log = logging.getLogger("controller.buffers.base.base")
|
||||
|
||||
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):
|
||||
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):
|
||||
@@ -29,11 +17,11 @@ class buffer(object):
|
||||
@ 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__()
|
||||
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.
|
||||
# Read more about compose functions in sessions/twitter/compose.py.
|
||||
self.compose_function = None
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
@@ -147,63 +135,4 @@ class buffer(object):
|
||||
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
|
||||
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
|
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
|
651
src/controller/buffers/twitter/base.py
Normal file
651
src/controller/buffers/twitter/base.py
Normal file
@@ -0,0 +1,651 @@
|
||||
# -*- 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.error import TweepError
|
||||
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 TweepError 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 TweepError 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 TweepError as e:
|
||||
log.error("Error %s: %s" % (e.api_code, e.reason))
|
||||
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 TweepError as e:
|
||||
log.error("Error %s: %s" % (e.api_code, e.reason))
|
||||
return
|
||||
if items == None:
|
||||
return
|
||||
items_db = self.session.db[self.name]
|
||||
self.session.add_users_from_results(items)
|
||||
for i in items:
|
||||
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i.id, self.session.db[self.name]) == None:
|
||||
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.destroy_direct_message(id=self.get_right_tweet().id)
|
||||
items.pop(index)
|
||||
else:
|
||||
self.session.twitter.destroy_status(id=self.get_right_tweet().id)
|
||||
items.pop(index)
|
||||
self.buffer.list.remove_item(index)
|
||||
except TweepError:
|
||||
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 open_in_browser(self, *args, **kwargs):
|
||||
tweet = self.get_tweet()
|
||||
output.speak(_(u"Opening item in web browser..."))
|
||||
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=self.session.get_user(tweet.user).screen_name, tweet_id=tweet.id)
|
||||
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.error import TweepError
|
||||
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 TweepError as e:
|
||||
log.error("Error %s: %s" % (e.api_code, e.reason))
|
||||
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.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
|
259
src/controller/buffers/twitter/people.py
Normal file
259
src/controller/buffers/twitter/people.py
Normal file
@@ -0,0 +1,259 @@
|
||||
# -*- 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.error import TweepError
|
||||
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 TweepError as e:
|
||||
log.error("Error %s: %s" % (e.api_code, e.reason))
|
||||
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 TweepError as e:
|
||||
log.error("Error %s: %s" % (e.api_code, e.reason))
|
||||
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 open_in_browser(self, *args, **kwargs):
|
||||
tweet = self.get_tweet()
|
||||
output.speak(_(u"Opening item in web browser..."))
|
||||
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
|
||||
webbrowser.open(url)
|
115
src/controller/buffers/twitter/search.py
Normal file
115
src/controller/buffers/twitter/search.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import platform
|
||||
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 . 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):
|
||||
# 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
|
||||
if start == True:
|
||||
self.statuses = []
|
||||
self.ids = []
|
||||
self.statuses.append(self.tweet)
|
||||
self.ids.append(self.tweet.id)
|
||||
tweet = self.tweet
|
||||
if not hasattr(tweet, "in_reply_to_status_id"):
|
||||
tweet.in_reply_to_status_id = None
|
||||
while tweet.in_reply_to_status_id != None:
|
||||
try:
|
||||
tweet = self.session.twitter.get_status(id=tweet.in_reply_to_status_id, tweet_mode="extended")
|
||||
except TweepError as err:
|
||||
break
|
||||
self.statuses.insert(0, tweet)
|
||||
self.ids.append(tweet.id)
|
||||
if tweet.in_reply_to_status_id == None:
|
||||
self.kwargs["since_id"] = tweet.id
|
||||
self.ids.append(tweet.id)
|
||||
val2 = self.session.search(self.name, tweet_mode="extended", *self.args, **self.kwargs)
|
||||
for i in val2:
|
||||
if i.in_reply_to_status_id in self.ids:
|
||||
self.statuses.append(i)
|
||||
self.ids.append(i.id)
|
||||
tweet = i
|
||||
number_of_items = self.session.order_buffer(self.name, self.statuses)
|
||||
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
|
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.error import TweepError
|
||||
from pubsub import pub
|
||||
from controller.buffers import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.twitter.trends")
|
||||
|
||||
class TrendsBuffer(base.Buffer):
|
||||
def __init__(self, parent, name, session, account, trendsFor, *args, **kwargs):
|
||||
super(TrendsBuffer, self).__init__(parent=parent, session=session)
|
||||
self.trendsFor = trendsFor
|
||||
self.session = session
|
||||
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.trends_place(id=self.trendsFor)
|
||||
except TweepError as err:
|
||||
log.error("Error %s: %s" % (err.api_code, err.reason))
|
||||
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
@@ -22,8 +22,7 @@ elif system == "Linux":
|
||||
from gtkUI import (view, commonMessageDialogs)
|
||||
from sessions.twitter import utils, compose
|
||||
from sessionmanager import manager, sessionManager
|
||||
|
||||
from controller.buffers import baseBuffers, twitterBuffers
|
||||
from controller import buffers
|
||||
from . import messages
|
||||
import sessions
|
||||
from sessions.twitter import session as session_
|
||||
@@ -126,6 +125,7 @@ class Controller(object):
|
||||
pub.subscribe(self.update_sent_dms, "sent-dms-updated")
|
||||
pub.subscribe(self.more_dms, "more-sent-dms")
|
||||
pub.subscribe(self.manage_sent_tweets, "sent-tweet")
|
||||
pub.subscribe(self.manage_new_tweet, "newTweet")
|
||||
pub.subscribe(self.manage_friend, "friend")
|
||||
pub.subscribe(self.manage_unfollowing, "unfollowing")
|
||||
pub.subscribe(self.manage_favourite, "favourite")
|
||||
@@ -266,15 +266,19 @@ class Controller(object):
|
||||
if sessions.sessions[i].is_logged == False: continue
|
||||
self.start_buffers(sessions.sessions[i])
|
||||
self.set_buffer_positions(sessions.sessions[i])
|
||||
sessions.sessions[i].start_streaming()
|
||||
if config.app["app-settings"]["play_ready_sound"] == True:
|
||||
sessions.sessions[list(sessions.sessions.keys())[0]].sound.play("ready.ogg")
|
||||
if config.app["app-settings"]["speak_ready_msg"] == True:
|
||||
output.speak(_(u"Ready"))
|
||||
self.started = True
|
||||
self.streams_checker_function = RepeatingTimer(60, self.check_streams)
|
||||
self.streams_checker_function.start()
|
||||
|
||||
|
||||
def create_ignored_session_buffer(self, session):
|
||||
self.accounts.append(session.settings["twitter"]["user_name"])
|
||||
account = baseBuffers.accountPanel(self.view.nb, session.settings["twitter"]["user_name"], session.settings["twitter"]["user_name"], session.session_id)
|
||||
account = buffers.base.AccountBuffer(self.view.nb, session.settings["twitter"]["user_name"], session.settings["twitter"]["user_name"], session.session_id)
|
||||
account.logged = False
|
||||
account.setup_account()
|
||||
self.buffers.append(account)
|
||||
@@ -294,96 +298,96 @@ class Controller(object):
|
||||
session.get_user_info()
|
||||
if createAccounts == True:
|
||||
self.accounts.append(session.db["user_name"])
|
||||
account = baseBuffers.accountPanel(self.view.nb, session.db["user_name"], session.db["user_name"], session.session_id)
|
||||
account = buffers.base.AccountBuffer(self.view.nb, session.db["user_name"], session.db["user_name"], session.session_id)
|
||||
account.setup_account()
|
||||
self.buffers.append(account)
|
||||
self.view.add_buffer(account.buffer , name=session.db["user_name"])
|
||||
for i in session.settings['general']['buffer_order']:
|
||||
if i == 'home':
|
||||
home = twitterBuffers.baseBufferController(self.view.nb, "home_timeline", "home_timeline", session, session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended")
|
||||
home = buffers.twitter.BaseBuffer(self.view.nb, "home_timeline", "home_timeline", session, session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended")
|
||||
self.buffers.append(home)
|
||||
self.view.insert_buffer(home.buffer, name=_(u"Home"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'mentions':
|
||||
mentions = twitterBuffers.baseBufferController(self.view.nb, "mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")
|
||||
mentions = buffers.twitter.BaseBuffer(self.view.nb, "mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")
|
||||
self.buffers.append(mentions)
|
||||
self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'dm':
|
||||
dm = twitterBuffers.directMessagesController(self.view.nb, "list_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg")
|
||||
dm = buffers.twitter.DirectMessagesBuffer(self.view.nb, "list_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg")
|
||||
self.buffers.append(dm)
|
||||
self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'sent_dm':
|
||||
sent_dm = twitterBuffers.sentDirectMessagesController(self.view.nb, "", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message")
|
||||
sent_dm = buffers.twitter.SentDirectMessagesBuffer(self.view.nb, "", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message")
|
||||
self.buffers.append(sent_dm)
|
||||
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'sent_tweets':
|
||||
sent_tweets = twitterBuffers.baseBufferController(self.view.nb, "user_timeline", "sent_tweets", session, session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended")
|
||||
sent_tweets = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "sent_tweets", session, session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended")
|
||||
self.buffers.append(sent_tweets)
|
||||
self.view.insert_buffer(sent_tweets.buffer, name=_(u"Sent tweets"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'favorites':
|
||||
favourites = twitterBuffers.baseBufferController(self.view.nb, "favorites", "favourites", session, session.db["user_name"], sound="favourite.ogg", tweet_mode="extended")
|
||||
favourites = buffers.twitter.BaseBuffer(self.view.nb, "favorites", "favourites", session, session.db["user_name"], sound="favourite.ogg", tweet_mode="extended")
|
||||
self.buffers.append(favourites)
|
||||
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'followers':
|
||||
followers = twitterBuffers.peopleBufferController(self.view.nb, "followers", "followers", session, session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"])
|
||||
followers = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "followers", session, session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"])
|
||||
self.buffers.append(followers)
|
||||
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'friends':
|
||||
friends = twitterBuffers.peopleBufferController(self.view.nb, "friends", "friends", session, session.db["user_name"], screen_name=session.db["user_name"])
|
||||
friends = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "friends", session, session.db["user_name"], screen_name=session.db["user_name"])
|
||||
self.buffers.append(friends)
|
||||
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'blocks':
|
||||
blocks = twitterBuffers.peopleBufferController(self.view.nb, "blocks", "blocked", session, session.db["user_name"])
|
||||
blocks = buffers.twitter.PeopleBuffer(self.view.nb, "blocks", "blocked", session, session.db["user_name"])
|
||||
self.buffers.append(blocks)
|
||||
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
elif i == 'muted':
|
||||
muted = twitterBuffers.peopleBufferController(self.view.nb, "mutes", "muted", session, session.db["user_name"])
|
||||
muted = buffers.twitter.PeopleBuffer(self.view.nb, "mutes", "muted", session, session.db["user_name"])
|
||||
self.buffers.append(muted)
|
||||
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
timelines = baseBuffers.emptyPanel(self.view.nb, "timelines", session.db["user_name"])
|
||||
timelines = buffers.base.EmptyBuffer(self.view.nb, "timelines", session.db["user_name"])
|
||||
self.buffers.append(timelines)
|
||||
self.view.insert_buffer(timelines.buffer , name=_(u"Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["timelines"]:
|
||||
tl = twitterBuffers.baseBufferController(self.view.nb, "user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended")
|
||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended")
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"]))
|
||||
favs_timelines = baseBuffers.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"])
|
||||
favs_timelines = buffers.base.EmptyBuffer(self.view.nb, "favs_timelines", session.db["user_name"])
|
||||
self.buffers.append(favs_timelines)
|
||||
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Likes timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["favourites_timelines"]:
|
||||
tl = twitterBuffers.baseBufferController(self.view.nb, "favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended")
|
||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended")
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
|
||||
followers_timelines = baseBuffers.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
|
||||
followers_timelines = buffers.base.EmptyBuffer(self.view.nb, "followers_timelines", session.db["user_name"])
|
||||
self.buffers.append(followers_timelines)
|
||||
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["followers_timelines"]:
|
||||
tl = twitterBuffers.peopleBufferController(self.view.nb, "followers", "%s-followers" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
|
||||
tl = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "%s-followers" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("followers_timelines", session.db["user_name"]))
|
||||
friends_timelines = baseBuffers.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
|
||||
friends_timelines = buffers.base.EmptyBuffer(self.view.nb, "friends_timelines", session.db["user_name"])
|
||||
self.buffers.append(friends_timelines)
|
||||
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Friends' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["friends_timelines"]:
|
||||
tl = twitterBuffers.peopleBufferController(self.view.nb, "friends", "%s-friends" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
|
||||
tl = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "%s-friends" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("friends_timelines", session.db["user_name"]))
|
||||
lists = baseBuffers.emptyPanel(self.view.nb, "lists", session.db["user_name"])
|
||||
lists = buffers.base.EmptyBuffer(self.view.nb, "lists", session.db["user_name"])
|
||||
self.buffers.append(lists)
|
||||
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["lists"]:
|
||||
tl = twitterBuffers.listBufferController(self.view.nb, "list_timeline", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")
|
||||
tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")
|
||||
session.lists.append(tl)
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(i), pos=self.view.search("lists", session.db["user_name"]))
|
||||
searches = baseBuffers.emptyPanel(self.view.nb, "searches", session.db["user_name"])
|
||||
searches = buffers.base.EmptyBuffer(self.view.nb, "searches", session.db["user_name"])
|
||||
self.buffers.append(searches)
|
||||
self.view.insert_buffer(searches.buffer , name=_(u"Searches"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["tweet_searches"]:
|
||||
tl = twitterBuffers.searchBufferController(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended")
|
||||
tl = buffers.twitter.SearchBuffer(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended")
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Search for {}").format(i), pos=self.view.search("searches", session.db["user_name"]))
|
||||
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
||||
buffer = twitterBuffers.trendsBufferController(self.view.nb, "%s_tt" % (i,), session, session.db["user_name"], i, sound="trends_updated.ogg")
|
||||
buffer = buffers.twitter.TrendsBuffer(self.view.nb, "%s_tt" % (i,), session, session.db["user_name"], i, sound="trends_updated.ogg")
|
||||
buffer.start_stream(play_sound=False)
|
||||
buffer.searchfunction = self.search
|
||||
self.buffers.append(buffer)
|
||||
@@ -431,12 +435,12 @@ class Controller(object):
|
||||
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
|
||||
buffer.session.settings.write()
|
||||
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
|
||||
search = twitterBuffers.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args)
|
||||
search = buffers.twitter.SearchBuffer(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args)
|
||||
else:
|
||||
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
|
||||
return
|
||||
elif dlg.get("users") == True:
|
||||
search = twitterBuffers.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term)
|
||||
search = buffers.twitter.SearchPeopleBuffer(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term)
|
||||
search.start_stream(mandatory=True)
|
||||
pos=self.view.search("searches", buffer.session.db["user_name"])
|
||||
self.insert_buffer(search, pos)
|
||||
@@ -650,6 +654,8 @@ class Controller(object):
|
||||
log.debug("Saving global configuration...")
|
||||
for item in sessions.sessions:
|
||||
if sessions.sessions[item].logged == False: continue
|
||||
log.debug("Disconnecting streaming endpoint for session" + sessions.sessions[item].session_id)
|
||||
sessions.sessions[item].stop_streaming()
|
||||
log.debug("Disconnecting streams for %s session" % (sessions.sessions[item].session_id,))
|
||||
sessions.sessions[item].sound.cleaner.cancel()
|
||||
log.debug("Saving database for " + sessions.sessions[item].session_id)
|
||||
@@ -659,6 +665,9 @@ class Controller(object):
|
||||
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
||||
if os.path.exists(pidpath):
|
||||
os.remove(pidpath)
|
||||
if hasattr(self, "streams_checker_function"):
|
||||
log.debug("Stopping stream checker...")
|
||||
self.streams_checker_function.cancel()
|
||||
widgetUtils.exit_application()
|
||||
|
||||
def follow(self, *args, **kwargs):
|
||||
@@ -849,7 +858,7 @@ class Controller(object):
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.baseBufferController(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, tweet_mode="extended")
|
||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, tweet_mode="extended")
|
||||
try:
|
||||
tl.start_stream(play_sound=False)
|
||||
except ValueError:
|
||||
@@ -868,7 +877,7 @@ class Controller(object):
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.baseBufferController(self.view.nb, "favorites", "%s-favorite" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, tweet_mode="extended")
|
||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "favorites", "%s-favorite" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, tweet_mode="extended")
|
||||
try:
|
||||
tl.start_stream(play_sound=False)
|
||||
except ValueError:
|
||||
@@ -887,7 +896,7 @@ class Controller(object):
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["followers_timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.peopleBufferController(self.view.nb, "followers", "%s-followers" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str)
|
||||
tl = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "%s-followers" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str)
|
||||
try:
|
||||
tl.start_stream(play_sound=False)
|
||||
except ValueError:
|
||||
@@ -906,7 +915,7 @@ class Controller(object):
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["friends_timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.peopleBufferController(self.view.nb, "friends", "%s-friends" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str)
|
||||
tl = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "%s-friends" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str)
|
||||
try:
|
||||
tl.start_stream(play_sound=False)
|
||||
except ValueError:
|
||||
@@ -926,7 +935,7 @@ class Controller(object):
|
||||
buffer = self.get_current_buffer()
|
||||
id = buffer.get_right_tweet().id
|
||||
user = buffer.session.get_user(buffer.get_right_tweet().user).screen_name
|
||||
search = twitterBuffers.conversationBufferController(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
|
||||
search = buffers.twitter.ConversationBuffer(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
|
||||
search.tweet = buffer.get_right_tweet()
|
||||
search.start_stream(start=True)
|
||||
pos=self.view.search("searches", buffer.session.db["user_name"])
|
||||
@@ -953,7 +962,7 @@ class Controller(object):
|
||||
if trends.dialog.get_response() == widgetUtils.OK:
|
||||
woeid = trends.get_woeid()
|
||||
if woeid in buff.session.settings["other_buffers"]["trending_topic_buffers"]: return
|
||||
buffer = twitterBuffers.trendsBufferController(self.view.nb, "%s_tt" % (woeid,), buff.session, buff.account, woeid, sound="trends_updated.ogg")
|
||||
buffer = buffers.twitter.TrendsBuffer(self.view.nb, "%s_tt" % (woeid,), buff.session, buff.account, woeid, sound="trends_updated.ogg")
|
||||
buffer.searchfunction = self.search
|
||||
pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])
|
||||
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (trends.get_string()), pos=pos)
|
||||
@@ -1269,8 +1278,6 @@ class Controller(object):
|
||||
def manage_sent_tweets(self, data, user):
|
||||
buffer = self.search_buffer("sent_tweets", user)
|
||||
if buffer == None: return
|
||||
# if "sent_tweets" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
||||
# self.notify(buffer.session, play_sound=play_sound)
|
||||
data = buffer.session.check_quoted_status(data)
|
||||
data = buffer.session.check_long_tweet(data)
|
||||
if data == False: # Long tweet deleted from twishort.
|
||||
@@ -1364,41 +1371,37 @@ class Controller(object):
|
||||
buff = self.search_buffer("home_timeline", account)
|
||||
if create == True:
|
||||
if buffer == "favourites":
|
||||
favourites = twitterBuffers.baseBufferController(self.view.nb, "favorites", "favourites", buff.session, buff.session.db["user_name"], tweet_mode="extended")
|
||||
favourites = buffers.twitter.BaseBuffer(self.view.nb, "favorites", "favourites", buff.session, buff.session.db["user_name"], tweet_mode="extended")
|
||||
self.buffers.append(favourites)
|
||||
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
favourites.start_stream(play_sound=False)
|
||||
if buffer == "followers":
|
||||
followers = twitterBuffers.peopleBufferController(self.view.nb, "followers", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
|
||||
followers = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
|
||||
self.buffers.append(followers)
|
||||
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
followers.start_stream(play_sound=False)
|
||||
elif buffer == "friends":
|
||||
friends = twitterBuffers.peopleBufferController(self.view.nb, "friends", "friends", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
|
||||
friends = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "friends", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
|
||||
self.buffers.append(friends)
|
||||
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
friends.start_stream(play_sound=False)
|
||||
elif buffer == "blocked":
|
||||
blocks = twitterBuffers.peopleBufferController(self.view.nb, "blocks", "blocked", buff.session, buff.session.db["user_name"])
|
||||
blocks = buffers.twitter.PeopleBuffer(self.view.nb, "blocks", "blocked", buff.session, buff.session.db["user_name"])
|
||||
self.buffers.append(blocks)
|
||||
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
blocks.start_stream(play_sound=False)
|
||||
elif buffer == "muted":
|
||||
muted = twitterBuffers.peopleBufferController(self.view.nb, "mutes", "muted", buff.session, buff.session.db["user_name"])
|
||||
muted = buffers.twitter.PeopleBuffer(self.view.nb, "mutes", "muted", buff.session, buff.session.db["user_name"])
|
||||
self.buffers.append(muted)
|
||||
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
muted.start_stream(play_sound=False)
|
||||
elif buffer == "events":
|
||||
events = twitterBuffers.eventsBufferController(self.view.nb, "events", buff.session, buff.session.db["user_name"], bufferType="dmPanel", screen_name=buff.session.db["user_name"])
|
||||
self.buffers.append(events)
|
||||
self.view.insert_buffer(events.buffer, name=_(u"Events"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
elif create == False:
|
||||
self.destroy_buffer(buffer, buff.session.db["user_name"])
|
||||
elif buffer == "list":
|
||||
if create in buff.session.settings["other_buffers"]["lists"]:
|
||||
output.speak(_(u"This list is already opened"), True)
|
||||
return
|
||||
tl = twitterBuffers.listBufferController(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended")
|
||||
tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended")
|
||||
buff.session.lists.append(tl)
|
||||
pos=self.view.search("lists", buff.session.db["user_name"])
|
||||
self.insert_buffer(tl, pos)
|
||||
@@ -1627,3 +1630,27 @@ class Controller(object):
|
||||
def save_data_in_db(self):
|
||||
for i in sessions.sessions:
|
||||
sessions.sessions[i].save_persistent_data()
|
||||
|
||||
def manage_new_tweet(self, data, user, _buffers):
|
||||
sound_to_play = None
|
||||
for buff in _buffers:
|
||||
buffer = self.search_buffer(buff, user)
|
||||
if buffer == None or buffer.session.db["user_name"] != user: return
|
||||
buffer.add_new_item(data)
|
||||
if buff == "home_timeline": sound_to_play = "tweet_received.ogg"
|
||||
elif buff == "mentions": sound_to_play = "mention_received.ogg"
|
||||
elif buff == "sent_tweets": sound_to_play = "tweet_send.ogg"
|
||||
elif "timeline" in buff: sound_to_play = "tweet_timeline.ogg"
|
||||
else: sound_to_play = None
|
||||
if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
||||
self.notify(buffer.session, sound_to_play)
|
||||
|
||||
def check_streams(self):
|
||||
if self.started == False:
|
||||
return
|
||||
for i in sessions.sessions:
|
||||
try:
|
||||
if sessions.sessions[i].is_logged == False: continue
|
||||
sessions.sessions[i].check_streams()
|
||||
except TweepError: # We shouldn't allow this function to die.
|
||||
pass
|
||||
|
@@ -27,7 +27,7 @@ class profileController(object):
|
||||
except TweepError as err:
|
||||
if err.api_code == 50:
|
||||
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
if err.api_code == 403:
|
||||
if err.api_code == 63:
|
||||
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
log.error("error %d: %s" % (err.api_code, err.reason))
|
||||
return
|
||||
|
@@ -40,25 +40,19 @@ def is_long(tweet):
|
||||
""" Check if the passed tweet is made with Twishort.
|
||||
returns True if is a long tweet, False otherwise."""
|
||||
long = False
|
||||
for url in range(0, len(tweet.entities["urls"])):
|
||||
try:
|
||||
if tweet.entities["urls"][url] != None and "twishort.com" in tweet.entities["urls"][url]["expanded_url"]:
|
||||
long = get_twishort_uri(tweet.entities["urls"][url]["expanded_url"])
|
||||
except IndexError:
|
||||
pass
|
||||
# sometimes Twitter returns URL's with None objects, so let's take it.
|
||||
# see https://github.com/manuelcortez/TWBlue/issues/103
|
||||
except TypeError:
|
||||
pass
|
||||
if long == False and hasattr(tweet, "retweeted_status"):
|
||||
for url in range(0, len(tweet.retweeted_status.entities["urls"])):
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("urls"):
|
||||
for url in range(0, len(tweet.entities["urls"])):
|
||||
try:
|
||||
if tweet.retweeted_status.entities["urls"][url] != None and "twishort.com" in tweet.retweeted_status.entities["urls"][url]["expanded_url"]:
|
||||
long = get_twishort_uri(tweet.retweeted_status.entities["urls"][url]["expanded_url"])
|
||||
if tweet.entities["urls"][url] != None and "twishort.com" in tweet.entities["urls"][url]["expanded_url"]:
|
||||
long = get_twishort_uri(tweet.entities["urls"][url]["expanded_url"])
|
||||
except IndexError:
|
||||
pass
|
||||
# sometimes Twitter returns URL's with None objects, so let's take it.
|
||||
# see https://github.com/manuelcortez/TWBlue/issues/103
|
||||
except TypeError:
|
||||
pass
|
||||
if long == False and hasattr(tweet, "retweeted_status"):
|
||||
return is_long(tweet.retweeted_status)
|
||||
return long
|
||||
|
||||
def get_full_text(uri):
|
||||
|
@@ -17,7 +17,7 @@ from keys import keyring
|
||||
from sessions import base
|
||||
from sessions.twitter import utils, compose
|
||||
from sessions.twitter.long_tweets import tweets, twishort
|
||||
from . import reduce
|
||||
from . import reduce, streaming
|
||||
from .wxUI import authorisationDialog
|
||||
|
||||
log = logging.getLogger("sessions.twitterSession")
|
||||
@@ -108,7 +108,6 @@ class Session(base.baseSession):
|
||||
self.db["sent_direct_messages"] = sent_objects
|
||||
pub.sendMessage("sent-dms-updated", total=sent, account=self.db["user_name"])
|
||||
|
||||
|
||||
return incoming
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -125,6 +124,8 @@ class Session(base.baseSession):
|
||||
# This will be especially useful because if the user reactivates their account later, TWblue will try to retrieve such user again at startup.
|
||||
# If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object.
|
||||
self.deleted_users = {}
|
||||
pub.subscribe(self.handle_new_status, "newStatus")
|
||||
pub.subscribe(self.handle_connected, "streamConnected")
|
||||
|
||||
# @_require_configuration
|
||||
def login(self, verify_credentials=True):
|
||||
@@ -501,3 +502,74 @@ class Session(base.baseSession):
|
||||
if hasattr(i, "retweeted_status") and (i.retweeted_status.user.id_str in self.db["users"]) == False:
|
||||
users[i.retweeted_status.user.id_str] = i.retweeted_status.user
|
||||
self.db["users"] = users
|
||||
|
||||
def start_streaming(self):
|
||||
if config.app["app-settings"]["no_streaming"]:
|
||||
return
|
||||
self.stream_listener = streaming.StreamListener(twitter_api=self.twitter, user=self.db["user_name"], user_id=self.db["user_id"], muted_users=self.db["muted_users"])
|
||||
self.stream = streaming.Stream(auth = self.auth, listener=self.stream_listener, chunk_size=1025)
|
||||
self.stream_thread = call_threaded(self.stream.filter, follow=self.stream_listener.users, stall_warnings=True)
|
||||
|
||||
def stop_streaming(self):
|
||||
if config.app["app-settings"]["no_streaming"]:
|
||||
return
|
||||
if hasattr(self, "stream"):
|
||||
self.stream.running = False
|
||||
log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
||||
|
||||
def handle_new_status(self, status, user):
|
||||
""" Handles a new status present in the Streaming API. """
|
||||
if self.logged == False:
|
||||
return
|
||||
# Discard processing the status if the streaming sends a tweet for another account.
|
||||
if self.db["user_name"] != user:
|
||||
return
|
||||
# the Streaming API sends non-extended tweets with an optional parameter "extended_tweets" which contains full_text and other data.
|
||||
# so we have to make sure we check it before processing the normal status.
|
||||
# As usual, we handle also quotes and retweets at first.
|
||||
if hasattr(status, "retweeted_status") and hasattr(status.retweeted_status, "extended_tweet"):
|
||||
status.retweeted_status._json = {**status.retweeted_status._json, **status.retweeted_status._json["extended_tweet"]}
|
||||
# compose.compose_tweet requires the parent tweet to have a full_text field, so we have to add it to retweets here.
|
||||
status._json["full_text"] = status._json["text"]
|
||||
if hasattr(status, "quoted_status") and hasattr(status.quoted_status, "extended_tweet"):
|
||||
status.quoted_status._json = {**status.quoted_status._json, **status.quoted_status._json["extended_tweet"]}
|
||||
if status.truncated:
|
||||
status._json = {**status._json, **status._json["extended_tweet"]}
|
||||
# Sends status to database, where it will be reduced and changed according to our needs.
|
||||
buffers_to_send = []
|
||||
if status.user.id_str in self.stream_listener.users:
|
||||
buffers_to_send.append("home_timeline")
|
||||
if status.user.id == self.db["user_id"]:
|
||||
buffers_to_send.append("sent_tweets")
|
||||
for user in status.entities["user_mentions"]:
|
||||
if user["id"] == self.db["user_id"]:
|
||||
buffers_to_send.append("mentions")
|
||||
users_with_timeline = [user.split("-")[0] for user in self.db.keys() if user.endswith("-timeline")]
|
||||
for user in users_with_timeline:
|
||||
if status.user.id_str == user:
|
||||
buffers_to_send.append("{}-timeline".format(user))
|
||||
for buffer in buffers_to_send[::]:
|
||||
num = self.order_buffer(buffer, [status])
|
||||
if num == 0:
|
||||
buffers_to_send.remove(buffer)
|
||||
# However, we have to do the "reduce and change" process here because the status we sent to the db is going to be a different object that the one sent to database.
|
||||
status = reduce.reduce_tweet(status)
|
||||
status = self.check_quoted_status(status)
|
||||
status = self.check_long_tweet(status)
|
||||
# Send it to the main controller object.
|
||||
pub.sendMessage("newTweet", data=status, user=self.db["user_name"], _buffers=buffers_to_send)
|
||||
|
||||
def check_streams(self):
|
||||
if config.app["app-settings"]["no_streaming"]:
|
||||
return
|
||||
if not hasattr(self, "stream"):
|
||||
return
|
||||
log.debug("Status of running stream for user {}: {}".format(self.db["user_name"], self.stream.running))
|
||||
if self.stream.running == False:
|
||||
self.start_streaming()
|
||||
|
||||
def handle_connected(self, user):
|
||||
if self.logged == False:
|
||||
return
|
||||
if user != self.db["user_name"]:
|
||||
log.debug("Connected streaming endpoint on account {}".format(user))
|
122
src/sessions/twitter/streaming.py
Normal file
122
src/sessions/twitter/streaming.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Streaming support for TWBlue. """
|
||||
import time
|
||||
import sys
|
||||
import six
|
||||
import requests
|
||||
import urllib3
|
||||
import ssl
|
||||
import tweepy
|
||||
import logging
|
||||
from pubsub import pub
|
||||
|
||||
log = logging.getLogger("sessions.twitter.streaming")
|
||||
|
||||
class StreamListener(tweepy.StreamListener):
|
||||
|
||||
def __init__(self, twitter_api, user, user_id, muted_users=[], *args, **kwargs):
|
||||
super(StreamListener, self).__init__(*args, **kwargs)
|
||||
log.debug("Starting streaming listener for account {}".format(user))
|
||||
self.started = False
|
||||
self.users = []
|
||||
self.api = twitter_api
|
||||
self.user = user
|
||||
self.user_id = user_id
|
||||
friends = self.api.friends_ids()
|
||||
log.debug("Retrieved {} friends to add to the streaming listener.".format(len(friends)))
|
||||
self.users.append(str(self.user_id))
|
||||
log.debug("Got {} muted users.".format(len(muted_users)))
|
||||
for user in friends:
|
||||
if user not in muted_users:
|
||||
self.users.append(str(user))
|
||||
self.started = True
|
||||
log.debug("Streaming listener started with {} users to follow.".format(len(self.users)))
|
||||
|
||||
def on_connect(self):
|
||||
pub.sendMessage("streamConnected", user=self.user)
|
||||
|
||||
def on_exception(self, ex):
|
||||
log.exception("Exception received on streaming endpoint for user {}".format(self.user))
|
||||
|
||||
def on_status(self, status):
|
||||
""" Checks data arriving as a tweet. """
|
||||
# Hide replies to users not followed by current account.
|
||||
if status.in_reply_to_user_id_str != None and status.in_reply_to_user_id_str not in self.users and status.user.screen_name != self.user:
|
||||
return
|
||||
if status.user.id_str in self.users:
|
||||
pub.sendMessage("newStatus", status=status, user=self.user)
|
||||
|
||||
|
||||
|
||||
class Stream(tweepy.Stream):
|
||||
|
||||
def _run(self):
|
||||
# Authenticate
|
||||
url = "https://%s%s" % (self.host, self.url)
|
||||
|
||||
# Connect and process the stream
|
||||
error_counter = 0
|
||||
resp = None
|
||||
exc_info = None
|
||||
while self.running:
|
||||
if self.retry_count is not None:
|
||||
if error_counter > self.retry_count:
|
||||
# quit if error count greater than retry count
|
||||
break
|
||||
try:
|
||||
auth = self.auth.apply_auth()
|
||||
resp = self.session.request('POST',
|
||||
url,
|
||||
data=self.body,
|
||||
timeout=self.timeout,
|
||||
stream=True,
|
||||
auth=auth,
|
||||
verify=self.verify,
|
||||
proxies = self.proxies)
|
||||
if resp.status_code != 200:
|
||||
if self.listener.on_error(resp.status_code) is False:
|
||||
break
|
||||
error_counter += 1
|
||||
if resp.status_code == 420:
|
||||
self.retry_time = max(self.retry_420_start,
|
||||
self.retry_time)
|
||||
time.sleep(self.retry_time)
|
||||
self.retry_time = min(self.retry_time * 2,
|
||||
self.retry_time_cap)
|
||||
else:
|
||||
error_counter = 0
|
||||
self.retry_time = self.retry_time_start
|
||||
self.snooze_time = self.snooze_time_step
|
||||
self.listener.on_connect()
|
||||
self._read_loop(resp)
|
||||
except (requests.ConnectionError, requests.Timeout, ssl.SSLError, urllib3.exceptions.ReadTimeoutError, urllib3.exceptions.ProtocolError) as exc:
|
||||
# This is still necessary, as a SSLError can actually be
|
||||
# thrown when using Requests
|
||||
# If it's not time out treat it like any other exception
|
||||
if isinstance(exc, ssl.SSLError):
|
||||
if not (exc.args and 'timed out' in str(exc.args[0])):
|
||||
exc_info = sys.exc_info()
|
||||
break
|
||||
if self.listener.on_timeout() is False:
|
||||
break
|
||||
if self.running is False:
|
||||
break
|
||||
time.sleep(self.snooze_time)
|
||||
self.snooze_time = min(self.snooze_time + self.snooze_time_step,
|
||||
self.snooze_time_cap)
|
||||
except Exception as exc:
|
||||
exc_info = sys.exc_info()
|
||||
# any other exception is fatal, so kill loop
|
||||
break
|
||||
|
||||
# cleanup
|
||||
self.running = False
|
||||
if resp:
|
||||
resp.close()
|
||||
|
||||
self.new_session()
|
||||
|
||||
if exc_info:
|
||||
# call a handler first so that the exception can be logged.
|
||||
self.listener.on_exception(exc_info[1])
|
||||
six.reraise(*exc_info)
|
@@ -41,9 +41,9 @@ def find_urls (tweet, twitter_media=False):
|
||||
if i["expanded_url"] not in urls:
|
||||
urls.append(i["expanded_url"])
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
urls.extend(find_urls(tweet.quoted_status))
|
||||
urls.extend(find_urls(tweet.quoted_status, twitter_media))
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
urls.extend(find_urls(tweet.retweeted_status))
|
||||
urls.extend(find_urls(tweet.retweeted_status, twitter_media))
|
||||
if hasattr(tweet, "message"):
|
||||
i = "message"
|
||||
elif hasattr(tweet, "full_text"):
|
||||
@@ -70,6 +70,14 @@ def find_list(name, lists):
|
||||
if lists[i].name == name: return lists[i].id
|
||||
|
||||
def is_audio(tweet):
|
||||
if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"):
|
||||
result = is_audio(tweet.quoted_status)
|
||||
if result != None:
|
||||
return result
|
||||
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities"):
|
||||
result = is_audio(tweet.retweeted_status)
|
||||
if result == True:
|
||||
return result
|
||||
# Checks firstly for Twitter videos and audios.
|
||||
if hasattr(tweet, "extended_entities"):
|
||||
for mediaItem in tweet.extended_entities["media"]:
|
||||
@@ -112,6 +120,10 @@ def is_media(tweet):
|
||||
def get_all_mentioned(tweet, conf, field="screen_name"):
|
||||
""" Gets all users that have been mentioned."""
|
||||
results = []
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
results.extend(get_all_mentionned(tweet.retweeted_status, conf, field))
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
results.extend(tweet.quoted_status, conf, field)
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||
for i in tweet.entities["user_mentions"]:
|
||||
if i["screen_name"] != conf["user_name"] and i["id_str"] != tweet.user:
|
||||
@@ -122,17 +134,19 @@ def get_all_mentioned(tweet, conf, field="screen_name"):
|
||||
def get_all_users(tweet, session):
|
||||
string = []
|
||||
user = session.get_user(tweet.user)
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
if user.screen_name != session.db["user_name"]:
|
||||
string.append(user.screen_name)
|
||||
tweet = tweet.retweeted_status
|
||||
else:
|
||||
if user.screen_name != session.db["user_name"]:
|
||||
string.append(user.screen_name)
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||
for i in tweet.entities["user_mentions"]:
|
||||
if i["screen_name"] != session.db["user_name"] and i["screen_name"] != user.screen_name:
|
||||
if i["screen_name"] not in string:
|
||||
string.append(i["screen_name"])
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
string.extend(get_all_users(tweet.retweeted_status, session))
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
string.extend(get_all_users(tweet.quoted_status, session))
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||
for i in tweet.entities["user_mentions"]:
|
||||
if i["screen_name"] != session.db["user_name"] and i["screen_name"] != user.screen_name:
|
||||
if i["screen_name"] not in string:
|
||||
string.append(i["screen_name"])
|
||||
# Attempt to remove duplicates, tipically caused by nested tweets.
|
||||
string = list(dict.fromkeys(string))
|
||||
if len(string) == 0:
|
||||
string.append(user.screen_name)
|
||||
return string
|
||||
@@ -154,7 +168,7 @@ def is_allowed(tweet, settings, buffer_name):
|
||||
tweet_data = {}
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
tweet_data["retweet"] = True
|
||||
if tweet.in_reply_to_status_id != None:
|
||||
if hasattr(tweet, "in_reply_to_status_id"):
|
||||
tweet_data["reply"] = True
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
tweet_data["quote"] = True
|
||||
|
@@ -1,5 +1,7 @@
|
||||
{"current_version": "6",
|
||||
{"current_version": "8",
|
||||
"description": "Snapshot version.",
|
||||
"date": "unknown",
|
||||
"downloads":
|
||||
{"Windows32": "https://twblue.es/pubs/snapshot.zip"}}
|
||||
{"Windows32": "https://twblue.es/pubs/twblue_snapshot_x86.zip",
|
||||
{"Windows64": "https://twblue.es/pubs/twblue_snapshot_x64.zip"
|
||||
}
|
Reference in New Issue
Block a user