mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2024-11-22 19:28:09 -06:00
Remove most of Twitter code as Twitter's API access has been removed
This commit is contained in:
parent
74fe437684
commit
8acebc290b
@ -32,7 +32,6 @@ twblue32:
|
|||||||
- cd ..\src
|
- cd ..\src
|
||||||
- '&$env:PYTHON ..\doc\generator.py'
|
- '&$env:PYTHON ..\doc\generator.py'
|
||||||
- '&$env:PYTHON write_version_data.py'
|
- '&$env:PYTHON write_version_data.py'
|
||||||
- New-Item "appkeys.py" -ItemType File -Value "twitter_api_key='$TWITTER_API_KEY'`ntwitter_api_secret='$TWITTER_API_SECRET'"
|
|
||||||
- '&$env:PYTHON setup.py build'
|
- '&$env:PYTHON setup.py build'
|
||||||
- cd ..
|
- cd ..
|
||||||
- mkdir artifacts
|
- mkdir artifacts
|
||||||
|
11
README.md
11
README.md
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/fml5fu7h1fj8vf6l?svg=true)](https://ci.appveyor.com/project/manuelcortez/twblue)
|
[![Build status](https://ci.appveyor.com/api/projects/status/fml5fu7h1fj8vf6l?svg=true)](https://ci.appveyor.com/project/manuelcortez/twblue)
|
||||||
|
|
||||||
TWBlue is a free and open source application that allows you to interact with the main features of Twitter and mastodon from the comfort of a windows software, with 2 different interfaces specially designed for screen reader users.
|
TWBlue is a free and open source application that allows you to interact with the main features of mastodon from the comfort of a windows software, with 2 different interfaces specially designed for screen reader users.
|
||||||
|
|
||||||
See [TWBlue's webpage](https://twblue.es) for more details.
|
See [TWBlue's webpage](https://twblue.es) for more details.
|
||||||
|
|
||||||
@ -11,15 +11,6 @@ See [TWBlue's webpage](https://twblue.es) for more details.
|
|||||||
|
|
||||||
This document describes how to run tw blue from source and how to build a binary version which doesn't need Python and the other dependencies to run.
|
This document describes how to run tw blue from source and how to build a binary version which doesn't need Python and the other dependencies to run.
|
||||||
|
|
||||||
### Generating application keys
|
|
||||||
|
|
||||||
In order to communicate with Twitter, you will need to generate a set of API keys in their [developer portal](https://developer.twitter.com/en/portal/dashboard) (If you haven't signed up, [visit this site to register as a developer](https://developer.twitter.com/en/docs/twitter-api/getting-started/getting-access-to-the-twitter-api)) and create a module called appkeys.py, within the src directory, with the following content, replacing the example values with your set of API keys:
|
|
||||||
|
|
||||||
```
|
|
||||||
twitter_api_key='xxxxxxxxxx'
|
|
||||||
twitter_api_secret='xxxxxxxxxx'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Required dependencies.
|
### Required dependencies.
|
||||||
|
|
||||||
Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash:
|
Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash:
|
||||||
|
@ -10,7 +10,6 @@ oauthlib
|
|||||||
requests-oauthlib
|
requests-oauthlib
|
||||||
requests-toolbelt
|
requests-toolbelt
|
||||||
pypubsub
|
pypubsub
|
||||||
geopy
|
|
||||||
arrow
|
arrow
|
||||||
python-dateutil
|
python-dateutil
|
||||||
winpaths
|
winpaths
|
||||||
@ -29,7 +28,6 @@ pywin32
|
|||||||
certifi
|
certifi
|
||||||
backports.functools_lru_cache
|
backports.functools_lru_cache
|
||||||
cx_freeze
|
cx_freeze
|
||||||
tweepy
|
|
||||||
twitter-text-parser
|
twitter-text-parser
|
||||||
mastodon.py
|
mastodon.py
|
||||||
pyenchant
|
pyenchant
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
[twitter]
|
|
||||||
user_key = string(default="")
|
|
||||||
user_secret = string(default="")
|
|
||||||
user_name = string(default="")
|
|
||||||
ignored_clients = list(default=list())
|
|
||||||
|
|
||||||
[general]
|
|
||||||
relative_times = boolean(default=True)
|
|
||||||
max_api_calls = integer(default=1)
|
|
||||||
max_tweets_per_call = integer(default=100)
|
|
||||||
reverse_timelines = boolean(default=False)
|
|
||||||
announce_stream_status = boolean(default=True)
|
|
||||||
retweet_mode = string(default="ask")
|
|
||||||
persist_size = integer(default=0)
|
|
||||||
load_cache_in_memory=boolean(default=True)
|
|
||||||
show_screen_names = boolean(default=False)
|
|
||||||
hide_emojis = boolean(default=False)
|
|
||||||
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted'))
|
|
||||||
|
|
||||||
[sound]
|
|
||||||
volume = float(default=1.0)
|
|
||||||
input_device = string(default="Default")
|
|
||||||
output_device = string(default="Default")
|
|
||||||
session_mute = boolean(default=False)
|
|
||||||
current_soundpack = string(default="FreakyBlue")
|
|
||||||
indicate_audio = boolean(default=True)
|
|
||||||
indicate_geo = boolean(default=True)
|
|
||||||
indicate_img = boolean(default=True)
|
|
||||||
sndup_api_key = string(default="")
|
|
||||||
|
|
||||||
[other_buffers]
|
|
||||||
timelines = list(default=list())
|
|
||||||
tweet_searches = list(default=list())
|
|
||||||
lists = list(default=list())
|
|
||||||
favourites_timelines = list(default=list())
|
|
||||||
followers_timelines = list(default=list())
|
|
||||||
friends_timelines = list(default=list())
|
|
||||||
trending_topic_buffers = list(default=list())
|
|
||||||
muted_buffers = list(default=list())
|
|
||||||
autoread_buffers = list(default=list(mentions, direct_messages, events))
|
|
||||||
|
|
||||||
[mysc]
|
|
||||||
spelling_language = string(default="")
|
|
||||||
save_followers_in_autocompletion_db = boolean(default=False)
|
|
||||||
save_friends_in_autocompletion_db = boolean(default=False)
|
|
||||||
ocr_language = string(default="")
|
|
||||||
|
|
||||||
[reporting]
|
|
||||||
braille_reporting = boolean(default=True)
|
|
||||||
speech_reporting = boolean(default=True)
|
|
||||||
|
|
||||||
[templates]
|
|
||||||
tweet = string(default="$display_name, $text $image_descriptions $date. $source")
|
|
||||||
dm = string(default="$sender_display_name, $text $date")
|
|
||||||
dm_sent = string(default="Dm to $recipient_display_name, $text $date")
|
|
||||||
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.")
|
|
||||||
|
|
||||||
[filters]
|
|
||||||
|
|
||||||
[user-aliases]
|
|
@ -1,12 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
|
||||||
|
|
||||||
# Make date check for feb 9.
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
end_of_twitter = datetime.datetime(2023, 2, 13)
|
|
||||||
twitter_support_enabled = True
|
|
||||||
#if now >= end_of_twitter:
|
|
||||||
# twitter_support_enabled = False
|
|
||||||
name = 'TWBlue'
|
name = 'TWBlue'
|
||||||
short_name='twblue'
|
short_name='twblue'
|
||||||
update_url = 'https://twblue.es/updates/updates.php'
|
update_url = 'https://twblue.es/updates/updates.php'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import base as base
|
from . import base as base
|
||||||
from . import twitter as twitter
|
|
||||||
from . import mastodon as mastodon
|
from . import mastodon as mastodon
|
@ -1,7 +0,0 @@
|
|||||||
# -*- 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
|
|
@ -1,663 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import time
|
|
||||||
import wx
|
|
||||||
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, templates
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from tweepy.cursor import Cursor
|
|
||||||
from pubsub import pub
|
|
||||||
from extra import ocr
|
|
||||||
from sessions.twitter.long_tweets import twishort, tweets
|
|
||||||
from wxUI import buffers, dialogs, commonMessageDialogs, menus
|
|
||||||
from controller.twitter import user, messages
|
|
||||||
|
|
||||||
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.twitter, bufferType)(parent, name)
|
|
||||||
else:
|
|
||||||
self.buffer = buffers.twitter.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):
|
|
||||||
title = _("Tweet")
|
|
||||||
caption = _("Write the tweet here")
|
|
||||||
tweet = messages.tweet(self.session, title, caption, "")
|
|
||||||
response = tweet.message.ShowModal()
|
|
||||||
if response == wx.ID_OK:
|
|
||||||
tweet_data = tweet.get_tweet_data()
|
|
||||||
call_threaded(self.session.send_tweet, *tweet_data)
|
|
||||||
if hasattr(tweet.message, "destroy"):
|
|
||||||
tweet.message.destroy()
|
|
||||||
|
|
||||||
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):
|
|
||||||
template = self.session.settings["templates"]["tweet"]
|
|
||||||
tweet = self.get_right_tweet()
|
|
||||||
t = templates.render_tweet(tweet, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
|
|
||||||
return t
|
|
||||||
|
|
||||||
def get_full_tweet(self):
|
|
||||||
tweet = self.get_right_tweet()
|
|
||||||
tweetsList = []
|
|
||||||
tweet_id = tweet.id
|
|
||||||
message = None
|
|
||||||
if hasattr(tweet, "message"):
|
|
||||||
message = tweet.message
|
|
||||||
try:
|
|
||||||
tweet = self.session.twitter.get_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities)
|
|
||||||
except TweepyException as e:
|
|
||||||
utils.twitter_error(e)
|
|
||||||
return
|
|
||||||
if message != None:
|
|
||||||
tweet.message = message
|
|
||||||
l = tweets.is_long(tweet)
|
|
||||||
while l != False:
|
|
||||||
tweetsList.append(tweet)
|
|
||||||
try:
|
|
||||||
tweet = self.session.twitter.get_status(id=l, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities)
|
|
||||||
except TweepyException as e:
|
|
||||||
utils.twitter_error(e)
|
|
||||||
return
|
|
||||||
l = tweets.is_long(tweet)
|
|
||||||
if l == False:
|
|
||||||
tweetsList.append(tweet)
|
|
||||||
return (tweet, tweetsList)
|
|
||||||
|
|
||||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
|
||||||
# starts stream every 3 minutes.
|
|
||||||
current_time = time.time()
|
|
||||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
|
|
||||||
self.execution_time = current_time
|
|
||||||
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
|
|
||||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
|
||||||
if self.name != "direct_messages":
|
|
||||||
val = self.session.call_paged(self.function, self.name, *self.args, **self.kwargs)
|
|
||||||
else:
|
|
||||||
# 50 results are allowed per API call, so let's assume max value can be 50.
|
|
||||||
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
|
|
||||||
if self.session.settings["general"]["max_tweets_per_call"] > 50:
|
|
||||||
count = 50
|
|
||||||
else:
|
|
||||||
count = self.session.settings["general"]["max_tweets_per_call"]
|
|
||||||
# try to retrieve the cursor for the current buffer.
|
|
||||||
try:
|
|
||||||
val = getattr(self.session.twitter, self.function)(return_cursors=True, count=count, *self.args, **self.kwargs)
|
|
||||||
if type(val) == tuple:
|
|
||||||
val, cursor = val
|
|
||||||
if type(cursor) == tuple:
|
|
||||||
cursor = cursor[1]
|
|
||||||
cursors = self.session.db["cursors"]
|
|
||||||
cursors[self.name] = cursor
|
|
||||||
self.session.db["cursors"] = cursors
|
|
||||||
results = [i for i in val]
|
|
||||||
val = results
|
|
||||||
val.reverse()
|
|
||||||
log.debug("Retrieved %d items from the cursored search on function %s." %(len(val), self.function))
|
|
||||||
user_ids = [item.message_create["sender_id"] for item in val]
|
|
||||||
self.session.save_users(user_ids)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error %s" % (str(e)))
|
|
||||||
return
|
|
||||||
number_of_items = self.session.order_buffer(self.name, val)
|
|
||||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
|
||||||
self.put_items_on_list(number_of_items)
|
|
||||||
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
|
||||||
if "-timeline" in self.name:
|
|
||||||
self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
|
|
||||||
elif "-favorite" in self.name:
|
|
||||||
self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
|
|
||||||
self.finished_timeline = True
|
|
||||||
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
|
||||||
self.session.sound.play(self.sound)
|
|
||||||
# Autoread settings
|
|
||||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
|
||||||
self.auto_read(number_of_items)
|
|
||||||
return number_of_items
|
|
||||||
|
|
||||||
def auto_read(self, number_of_items):
|
|
||||||
if number_of_items == 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
tweet = self.session.db[self.name][-1]
|
|
||||||
else:
|
|
||||||
tweet = self.session.db[self.name][0]
|
|
||||||
output.speak(_(u"New tweet in {0}").format(self.get_buffer_name()))
|
|
||||||
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
|
|
||||||
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
|
||||||
output.speak(_(u"{0} new tweets in {1}.").format(number_of_items, self.get_buffer_name()))
|
|
||||||
|
|
||||||
def get_more_items(self):
|
|
||||||
elements = []
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
last_id = self.session.db[self.name][0].id
|
|
||||||
else:
|
|
||||||
last_id = self.session.db[self.name][-1].id
|
|
||||||
try:
|
|
||||||
items = getattr(self.session.twitter, self.function)(max_id=last_id, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error %s" % (str(e)))
|
|
||||||
return
|
|
||||||
if items == None:
|
|
||||||
return
|
|
||||||
items_db = self.session.db[self.name]
|
|
||||||
self.session.add_users_from_results(items)
|
|
||||||
for i in items:
|
|
||||||
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i, self.session.db[self.name]) == None:
|
|
||||||
i = reduce.reduce_tweet(i)
|
|
||||||
i = self.session.check_quoted_status(i)
|
|
||||||
i = self.session.check_long_tweet(i)
|
|
||||||
elements.append(i)
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
items_db.insert(0, i)
|
|
||||||
else:
|
|
||||||
items_db.append(i)
|
|
||||||
self.session.db[self.name] = items_db
|
|
||||||
selection = self.buffer.list.get_selected()
|
|
||||||
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
for i in elements:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
else:
|
|
||||||
for i in items:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
self.buffer.list.select_item(selection)
|
|
||||||
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
|
||||||
|
|
||||||
def remove_buffer(self, force=False):
|
|
||||||
if "-timeline" in self.name:
|
|
||||||
if force == False:
|
|
||||||
dlg = commonMessageDialogs.remove_buffer()
|
|
||||||
else:
|
|
||||||
dlg = widgetUtils.YES
|
|
||||||
if dlg == widgetUtils.YES:
|
|
||||||
if self.name[:-9] in self.session.settings["other_buffers"]["timelines"]:
|
|
||||||
self.session.settings["other_buffers"]["timelines"].remove(self.name[:-9])
|
|
||||||
self.session.settings.write()
|
|
||||||
if self.name in self.session.db:
|
|
||||||
self.session.db.pop(self.name)
|
|
||||||
return True
|
|
||||||
elif dlg == widgetUtils.NO:
|
|
||||||
return False
|
|
||||||
elif "favorite" in self.name:
|
|
||||||
if force == False:
|
|
||||||
dlg = commonMessageDialogs.remove_buffer()
|
|
||||||
else:
|
|
||||||
dlg = widgetUtils.YES
|
|
||||||
if dlg == widgetUtils.YES:
|
|
||||||
if self.name[:-9] in self.session.settings["other_buffers"]["favourites_timelines"]:
|
|
||||||
self.session.settings["other_buffers"]["favourites_timelines"].remove(self.name[:-9])
|
|
||||||
if self.name in self.session.db:
|
|
||||||
self.session.db.pop(self.name)
|
|
||||||
self.session.settings.write()
|
|
||||||
return True
|
|
||||||
elif dlg == widgetUtils.NO:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove_tweet(self, id):
|
|
||||||
if type(self.session.db[self.name]) == dict: return
|
|
||||||
items = self.session.db[self.name]
|
|
||||||
for i in range(0, len(items)):
|
|
||||||
if items[i].id == id:
|
|
||||||
items.pop(i)
|
|
||||||
self.remove_item(i)
|
|
||||||
self.session.db[self.name] = items
|
|
||||||
|
|
||||||
def put_items_on_list(self, number_of_items):
|
|
||||||
list_to_use = self.session.db[self.name]
|
|
||||||
if number_of_items == 0 and self.session.settings["general"]["persist_size"] == 0: return
|
|
||||||
log.debug("The list contains %d items " % (self.buffer.list.get_count(),))
|
|
||||||
log.debug("Putting %d items on the list" % (number_of_items,))
|
|
||||||
if self.buffer.list.get_count() == 0:
|
|
||||||
for i in list_to_use:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
|
||||||
elif self.buffer.list.get_count() > 0 and number_of_items > 0:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
items = list_to_use[len(list_to_use)-number_of_items:]
|
|
||||||
for i in items:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
else:
|
|
||||||
items = list_to_use[0:number_of_items]
|
|
||||||
items.reverse()
|
|
||||||
for i in items:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
|
|
||||||
|
|
||||||
def add_new_item(self, item):
|
|
||||||
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
else:
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
|
||||||
output.speak(" ".join(tweet[:2]), speech=self.session.settings["reporting"]["speech_reporting"], braille=self.session.settings["reporting"]["braille_reporting"])
|
|
||||||
|
|
||||||
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)
|
|
||||||
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
|
|
||||||
|
|
||||||
def can_share(self):
|
|
||||||
tweet = self.get_right_tweet()
|
|
||||||
user = self.session.get_user(tweet.user)
|
|
||||||
is_protected = user.protected
|
|
||||||
return is_protected==False
|
|
||||||
|
|
||||||
@_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
|
|
||||||
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, _("Reply to %s") % (screen_name,), "", users=users, ids=ids)
|
|
||||||
if message.message.ShowModal() == widgetUtils.OK:
|
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
|
||||||
if len(users) > 0:
|
|
||||||
config.app["app-settings"]["mention_all"] = message.message.mention_all.GetValue()
|
|
||||||
config.app.write()
|
|
||||||
tweet_data = dict(text=message.message.text.GetValue(), attachments=message.attachments, poll_options=message.poll_options, poll_period=message.poll_period)
|
|
||||||
call_threaded(self.session.reply, in_reply_to_status_id=id, text=message.message.text.GetValue(), attachments=message.attachments, exclude_reply_user_ids=message.get_ids())
|
|
||||||
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, _("Direct message to %s") % (screen_name,), _("New direct message"), users)
|
|
||||||
if dm.message.ShowModal() == widgetUtils.OK:
|
|
||||||
screen_name = dm.message.cb.GetValue()
|
|
||||||
user = self.session.get_user_by_screen_name(screen_name)
|
|
||||||
recipient_id = user
|
|
||||||
text = dm.message.text.GetValue()
|
|
||||||
if len(dm.attachments) > 0:
|
|
||||||
attachment = dm.attachments[0]
|
|
||||||
else:
|
|
||||||
attachment = None
|
|
||||||
call_threaded(self.session.direct_message, text=text, recipient=recipient_id, attachment=attachment)
|
|
||||||
if hasattr(dm.message, "destroy"): dm.message.destroy()
|
|
||||||
|
|
||||||
@_tweets_exist
|
|
||||||
def share_item(self, *args, **kwargs):
|
|
||||||
if self.can_share() == False:
|
|
||||||
return output.speak(_("This action is not supported on protected accounts."))
|
|
||||||
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):
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
tweet = tweet.retweeted_status
|
|
||||||
retweet = messages.tweet(session=self.session, title=_("Quote"), caption=_("Add your comment to the tweet"), thread_mode=False)
|
|
||||||
if retweet.message.ShowModal() == widgetUtils.OK:
|
|
||||||
text = retweet.message.text.GetValue()
|
|
||||||
tweet_data = dict(text=text, attachments=retweet.attachments, poll_period=retweet.poll_period, poll_options=retweet.poll_options)
|
|
||||||
tweet_data.update(quote_tweet_id=id)
|
|
||||||
call_threaded(self.session.send_tweet, *[tweet_data])
|
|
||||||
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)
|
|
||||||
|
|
||||||
def onFocus(self, *args, **kwargs):
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
if 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")
|
|
||||||
can_share = self.can_share()
|
|
||||||
pub.sendMessage("toggleShare", shareable=can_share)
|
|
||||||
self.buffer.retweet.Enable(can_share)
|
|
||||||
|
|
||||||
def audio(self, url='', *args, **kwargs):
|
|
||||||
if sound.URLPlayer.player.is_playing():
|
|
||||||
return sound.URLPlayer.stop_audio()
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
if tweet == None: return
|
|
||||||
urls = utils.find_urls(tweet, twitter_media=True)
|
|
||||||
if len(urls) == 1:
|
|
||||||
url=urls[0]
|
|
||||||
elif len(urls) > 1:
|
|
||||||
urls_list = dialogs.urlList.urlList()
|
|
||||||
urls_list.populate_list(urls)
|
|
||||||
if urls_list.get_response() == widgetUtils.OK:
|
|
||||||
url=urls_list.get_string()
|
|
||||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
|
||||||
if url != '':
|
|
||||||
# try:
|
|
||||||
sound.URLPlayer.play(url, self.session.settings["sound"]["volume"])
|
|
||||||
# except:
|
|
||||||
# log.error("Exception while executing audio method.")
|
|
||||||
|
|
||||||
# @_tweets_exist
|
|
||||||
def url(self, url='', announce=True, *args, **kwargs):
|
|
||||||
if url == '':
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
urls = utils.find_urls(tweet)
|
|
||||||
if len(urls) == 1:
|
|
||||||
url=urls[0]
|
|
||||||
elif len(urls) > 1:
|
|
||||||
urls_list = dialogs.urlList.urlList()
|
|
||||||
urls_list.populate_list(urls)
|
|
||||||
if urls_list.get_response() == widgetUtils.OK:
|
|
||||||
url=urls_list.get_string()
|
|
||||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
|
||||||
if url != '':
|
|
||||||
if announce:
|
|
||||||
output.speak(_(u"Opening URL..."), True)
|
|
||||||
webbrowser.open_new_tab(url)
|
|
||||||
|
|
||||||
def clear_list(self):
|
|
||||||
dlg = commonMessageDialogs.clear_list()
|
|
||||||
if dlg == widgetUtils.YES:
|
|
||||||
self.session.db[self.name] = []
|
|
||||||
self.buffer.list.clear()
|
|
||||||
|
|
||||||
@_tweets_exist
|
|
||||||
def destroy_status(self, *args, **kwargs):
|
|
||||||
index = self.buffer.list.get_selected()
|
|
||||||
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
|
|
||||||
answer = commonMessageDialogs.delete_tweet_dialog(None)
|
|
||||||
if answer == widgetUtils.YES:
|
|
||||||
items = self.session.db[self.name]
|
|
||||||
try:
|
|
||||||
if self.name == "direct_messages" or self.name == "sent_direct_messages":
|
|
||||||
self.session.twitter.delete_direct_message(id=self.get_right_tweet().id)
|
|
||||||
items.pop(index)
|
|
||||||
else:
|
|
||||||
self.session.twitter.destroy_status(id=self.get_right_tweet().id)
|
|
||||||
items.pop(index)
|
|
||||||
self.buffer.list.remove_item(index)
|
|
||||||
except TweepyException:
|
|
||||||
self.session.sound.play("error.ogg")
|
|
||||||
self.session.db[self.name] = items
|
|
||||||
|
|
||||||
@_tweets_exist
|
|
||||||
def user_details(self):
|
|
||||||
tweet = self.get_right_tweet()
|
|
||||||
if self.type == "dm":
|
|
||||||
users = [self.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
elif self.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, self.session)
|
|
||||||
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
|
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
|
||||||
user.profileController(session=self.session, user=dlg.get_user())
|
|
||||||
if hasattr(dlg, "destroy"): dlg.destroy()
|
|
||||||
|
|
||||||
def get_quoted_tweet(self, tweet):
|
|
||||||
quoted_tweet = self.session.twitter.get_status(id=tweet.id)
|
|
||||||
quoted_tweet.text = utils.find_urls_in_text(quoted_tweet.text, quoted_tweet.entities)
|
|
||||||
l = tweets.is_long(quoted_tweet)
|
|
||||||
id = tweets.get_id(l)
|
|
||||||
original_tweet = self.session.twitter.get_status(id=id)
|
|
||||||
original_tweet.text = utils.find_urls_in_text(original_tweet.text, original_tweet.entities)
|
|
||||||
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
|
|
||||||
|
|
||||||
def get_item_url(self):
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=self.session.get_user(tweet.user).screen_name, tweet_id=tweet.id)
|
|
||||||
return url
|
|
||||||
|
|
||||||
def open_in_browser(self, *args, **kwargs):
|
|
||||||
url = self.get_item_url()
|
|
||||||
output.speak(_(u"Opening item in web browser..."))
|
|
||||||
webbrowser.open(url)
|
|
||||||
|
|
||||||
def add_to_favorites(self):
|
|
||||||
id = self.get_tweet().id
|
|
||||||
call_threaded(self.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
|
|
||||||
|
|
||||||
def remove_from_favorites(self):
|
|
||||||
id = self.get_tweet().id
|
|
||||||
call_threaded(self.session.api_call, call_name="destroy_favorite", id=id)
|
|
||||||
|
|
||||||
def toggle_favorite(self):
|
|
||||||
id = self.get_tweet().id
|
|
||||||
tweet = self.session.twitter.get_status(id=id, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
if tweet.favorited == False:
|
|
||||||
call_threaded(self.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
|
|
||||||
else:
|
|
||||||
call_threaded(self.session.api_call, call_name="destroy_favorite", id=id)
|
|
||||||
|
|
||||||
def view_item(self):
|
|
||||||
if self.type == "dm" or self.name == "direct_messages":
|
|
||||||
non_tweet = self.get_formatted_message()
|
|
||||||
item = self.get_right_tweet()
|
|
||||||
original_date = arrow.get(int(item.created_timestamp))
|
|
||||||
date = original_date.shift(seconds=self.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
|
|
||||||
msg = messages.viewTweet(non_tweet, [], False, date=date)
|
|
||||||
else:
|
|
||||||
tweet, tweetsList = self.get_full_tweet()
|
|
||||||
msg = messages.viewTweet(tweet, tweetsList, utc_offset=self.session.db["utc_offset"], item_url=self.get_item_url())
|
|
||||||
|
|
||||||
def reverse_geocode(self, geocoder):
|
|
||||||
try:
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
if tweet.coordinates != None:
|
|
||||||
x = tweet.coordinates["coordinates"][0]
|
|
||||||
y = tweet.coordinates["coordinates"][1]
|
|
||||||
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
|
|
||||||
return address
|
|
||||||
else:
|
|
||||||
output.speak(_("There are no coordinates in this tweet"))
|
|
||||||
# except GeocoderError:
|
|
||||||
# output.speak(_(u"There are no results for the coordinates in this tweet"))
|
|
||||||
except ValueError:
|
|
||||||
output.speak(_(u"Error decoding coordinates. Try again later."))
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def ocr_image(self):
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
media_list = []
|
|
||||||
if hasattr(tweet, "entities") and tweet.entities.get("media") != None:
|
|
||||||
[media_list.append(i) for i in tweet.entities["media"] if i not in media_list]
|
|
||||||
elif hasattr(tweet, "retweeted_status") and tweet.retweeted_status.get("media") != None:
|
|
||||||
[media_list.append(i) for i in tweet.retweeted_status.entities["media"] if i not in media_list]
|
|
||||||
elif hasattr(tweet, "quoted_status") and tweet.quoted_status.entities.get("media") != None:
|
|
||||||
[media_list.append(i) for i in tweet.quoted_status.entities["media"] if i not in media_list]
|
|
||||||
if len(media_list) > 1:
|
|
||||||
image_list = [_(u"Picture {0}").format(i,) for i in range(0, len(media_list))]
|
|
||||||
dialog = dialogs.urlList.urlList(title=_(u"Select the picture"))
|
|
||||||
if dialog.get_response() == widgetUtils.OK:
|
|
||||||
img = media_list[dialog.get_item()]
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
elif len(media_list) == 1:
|
|
||||||
img = media_list[0]
|
|
||||||
else:
|
|
||||||
output.speak(_(u"Invalid buffer"))
|
|
||||||
return
|
|
||||||
if self.session.settings["mysc"]["ocr_language"] != "":
|
|
||||||
ocr_lang = self.session.settings["mysc"]["ocr_language"]
|
|
||||||
else:
|
|
||||||
ocr_lang = ocr.OCRSpace.short_langs.index(tweet.lang)
|
|
||||||
ocr_lang = ocr.OCRSpace.OcrLangs[ocr_lang]
|
|
||||||
api = ocr.OCRSpace.OCRSpaceAPI()
|
|
||||||
try:
|
|
||||||
text = api.OCR_URL(img["media_url"], lang=ocr_lang)
|
|
||||||
except ocr.OCRSpace.APIError as er:
|
|
||||||
output.speak(_(u"Unable to extract text"))
|
|
||||||
return
|
|
||||||
msg = messages.viewTweet(text["ParsedText"], [], False)
|
|
@ -1,164 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import widgetUtils
|
|
||||||
import arrow
|
|
||||||
import webbrowser
|
|
||||||
import output
|
|
||||||
import config
|
|
||||||
import languageHandler
|
|
||||||
import logging
|
|
||||||
from sessions.twitter import compose, utils, templates
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from pubsub import pub
|
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
from controller.twitter import messages
|
|
||||||
from . import base
|
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.dmBuffer")
|
|
||||||
|
|
||||||
class DirectMessagesBuffer(base.BaseBuffer):
|
|
||||||
|
|
||||||
def get_more_items(self):
|
|
||||||
# 50 results are allowed per API call, so let's assume max value can be 50.
|
|
||||||
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
|
|
||||||
if self.session.settings["general"]["max_tweets_per_call"] > 50:
|
|
||||||
count = 50
|
|
||||||
else:
|
|
||||||
count = self.session.settings["general"]["max_tweets_per_call"]
|
|
||||||
total = 0
|
|
||||||
# try to retrieve the cursor for the current buffer.
|
|
||||||
cursor = self.session.db["cursors"].get(self.name)
|
|
||||||
try:
|
|
||||||
items = getattr(self.session.twitter, self.function)(return_cursors=True, cursor=cursor, count=count, *self.args, **self.kwargs)
|
|
||||||
if type(items) == tuple:
|
|
||||||
items, cursor = items
|
|
||||||
if type(cursor) == tuple:
|
|
||||||
cursor = cursor[1]
|
|
||||||
cursors = self.session.db["cursors"]
|
|
||||||
cursors[self.name] = cursor
|
|
||||||
self.session.db["cursors"] = cursors
|
|
||||||
results = [i for i in items]
|
|
||||||
items = results
|
|
||||||
log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function))
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error %s" % (str(e)))
|
|
||||||
return
|
|
||||||
if items == None:
|
|
||||||
return
|
|
||||||
sent = []
|
|
||||||
received = []
|
|
||||||
sent_dms = self.session.db["sent_direct_messages"]
|
|
||||||
received_dms = self.session.db["direct_messages"]
|
|
||||||
for i in items:
|
|
||||||
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
sent_dms.insert(0, i)
|
|
||||||
sent.append(i)
|
|
||||||
else:
|
|
||||||
sent_dms.append(i)
|
|
||||||
sent.insert(0, i)
|
|
||||||
else:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
received_dms.insert(0, i)
|
|
||||||
received.append(i)
|
|
||||||
else:
|
|
||||||
received_dms.append(i)
|
|
||||||
received.insert(0, i)
|
|
||||||
total = total+1
|
|
||||||
self.session.db["direct_messages"] = received_dms
|
|
||||||
self.session.db["sent_direct_messages"] = sent_dms
|
|
||||||
user_ids = [item.message_create["sender_id"] for item in items]
|
|
||||||
self.session.save_users(user_ids)
|
|
||||||
pub.sendMessage("twitter.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):
|
|
||||||
return self.send_message()
|
|
||||||
|
|
||||||
def onFocus(self, *args, **kwargs):
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
if 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."))
|
|
||||||
|
|
||||||
def get_message(self):
|
|
||||||
template = self.session.settings["templates"]["dm"]
|
|
||||||
dm = self.get_right_tweet()
|
|
||||||
t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
|
|
||||||
return t
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def get_message(self):
|
|
||||||
template = self.session.settings["templates"]["dm_sent"]
|
|
||||||
dm = self.get_right_tweet()
|
|
||||||
t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
|
|
||||||
return t
|
|
||||||
|
|
||||||
def view_item(self):
|
|
||||||
non_tweet = self.get_formatted_message()
|
|
||||||
item = self.get_right_tweet()
|
|
||||||
original_date = arrow.get(int(item.created_timestamp))
|
|
||||||
date = original_date.shift(seconds=self.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
|
|
||||||
msg = messages.viewTweet(non_tweet, [], False, date=date)
|
|
@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import widgetUtils
|
|
||||||
import logging
|
|
||||||
from tweepy.cursor import Cursor
|
|
||||||
from wxUI import dialogs, commonMessageDialogs
|
|
||||||
from . import base
|
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.listBuffer")
|
|
||||||
|
|
||||||
class ListBuffer(base.BaseBuffer):
|
|
||||||
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, list_id=None, *args, **kwargs):
|
|
||||||
super(ListBuffer, self).__init__(parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs)
|
|
||||||
self.users = []
|
|
||||||
self.list_id = list_id
|
|
||||||
self.kwargs["list_id"] = list_id
|
|
||||||
|
|
||||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
|
||||||
self.get_user_ids()
|
|
||||||
super(ListBuffer, self).start_stream(mandatory, play_sound, avoid_autoreading)
|
|
||||||
|
|
||||||
def get_user_ids(self):
|
|
||||||
for i in Cursor(self.session.twitter.get_list_members, list_id=self.list_id, include_entities=False, skip_status=True, count=5000).items():
|
|
||||||
if i.id not in self.users:
|
|
||||||
self.users.append(i.id)
|
|
||||||
|
|
||||||
def remove_buffer(self, force=False):
|
|
||||||
if force == False:
|
|
||||||
dlg = commonMessageDialogs.remove_buffer()
|
|
||||||
else:
|
|
||||||
dlg = widgetUtils.YES
|
|
||||||
if dlg == widgetUtils.YES:
|
|
||||||
if self.name[:-5] in self.session.settings["other_buffers"]["lists"]:
|
|
||||||
self.session.settings["other_buffers"]["lists"].remove(self.name[:-5])
|
|
||||||
if self.name in self.session.db:
|
|
||||||
self.session.db.pop(self.name)
|
|
||||||
self.session.settings.write()
|
|
||||||
return True
|
|
||||||
elif dlg == widgetUtils.NO:
|
|
||||||
return False
|
|
@ -1,254 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import time
|
|
||||||
import widgetUtils
|
|
||||||
import webbrowser
|
|
||||||
import output
|
|
||||||
import config
|
|
||||||
import logging
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from pubsub import pub
|
|
||||||
from controller.twitter import user, messages
|
|
||||||
from sessions.twitter import compose, templates
|
|
||||||
from wxUI import commonMessageDialogs, menus
|
|
||||||
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):
|
|
||||||
template = self.session.settings["templates"]["person"]
|
|
||||||
user = self.get_right_tweet()
|
|
||||||
t = templates.render_person(user, template, self.session, relative_times=True, offset_seconds=self.session.db["utc_offset"])
|
|
||||||
return t
|
|
||||||
|
|
||||||
def delete_item(self): pass
|
|
||||||
|
|
||||||
@_tweets_exist
|
|
||||||
def reply(self, *args, **kwargs):
|
|
||||||
tweet = self.get_right_tweet()
|
|
||||||
screen_name = tweet.screen_name
|
|
||||||
message = messages.tweet(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False)
|
|
||||||
if message.message.ShowModal() == widgetUtils.OK:
|
|
||||||
tweet_data = message.get_tweet_data()
|
|
||||||
call_threaded(self.session.send_tweet, *tweet_data)
|
|
||||||
if hasattr(message.message, "destroy"):
|
|
||||||
message.message.destroy()
|
|
||||||
|
|
||||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
|
||||||
# starts stream every 3 minutes.
|
|
||||||
current_time = time.time()
|
|
||||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
|
|
||||||
self.execution_time = current_time
|
|
||||||
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
|
|
||||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
|
||||||
try:
|
|
||||||
val = getattr(self.session.twitter, self.function)(return_cursors=True, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
|
|
||||||
if type(val) == tuple:
|
|
||||||
val, cursor = val
|
|
||||||
if type(cursor) == tuple:
|
|
||||||
cursor = cursor[1]
|
|
||||||
cursors = self.session.db["cursors"]
|
|
||||||
cursors[self.name] = cursor
|
|
||||||
self.session.db["cursors"] = cursors
|
|
||||||
results = [i for i in val]
|
|
||||||
val = results
|
|
||||||
val.reverse()
|
|
||||||
log.debug("Retrieved %d items from cursored search in function %s" % (len(val), self.function))
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error %s" % (str(e)))
|
|
||||||
return
|
|
||||||
number_of_items = self.session.order_people(self.name, val)
|
|
||||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
|
||||||
self.put_items_on_list(number_of_items)
|
|
||||||
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
|
||||||
self.username = self.session.api_call("get_user", **self.kwargs).screen_name
|
|
||||||
self.finished_timeline = True
|
|
||||||
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
|
||||||
self.session.sound.play(self.sound)
|
|
||||||
# Autoread settings
|
|
||||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
|
||||||
self.auto_read(number_of_items)
|
|
||||||
return number_of_items
|
|
||||||
|
|
||||||
def get_more_items(self):
|
|
||||||
try:
|
|
||||||
cursor = self.session.db["cursors"].get(self.name)
|
|
||||||
items = getattr(self.session.twitter, self.function)(return_cursors=True, users=True, cursor=cursor, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
|
|
||||||
if type(items) == tuple:
|
|
||||||
items, cursor = items
|
|
||||||
if type(cursor) == tuple:
|
|
||||||
cursor = cursor[1]
|
|
||||||
cursors = self.session.db["cursors"]
|
|
||||||
cursors[self.name] = cursor
|
|
||||||
self.session.db["cursors"] = cursors
|
|
||||||
results = [i for i in items]
|
|
||||||
items = results
|
|
||||||
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error %s" % (str(e)))
|
|
||||||
return
|
|
||||||
if items == None:
|
|
||||||
return
|
|
||||||
items_db = self.session.db[self.name]
|
|
||||||
for i in items:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
items_db.insert(0, i)
|
|
||||||
else:
|
|
||||||
items_db.append(i)
|
|
||||||
self.session.db[self.name] = items_db
|
|
||||||
selected = self.buffer.list.get_selected()
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == True:
|
|
||||||
for i in items:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
self.buffer.list.select_item(selected)
|
|
||||||
else:
|
|
||||||
for i in items:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
output.speak(_(u"%s items retrieved") % (len(items)), True)
|
|
||||||
|
|
||||||
def put_items_on_list(self, number_of_items):
|
|
||||||
log.debug("The list contains %d items" % (self.buffer.list.get_count(),))
|
|
||||||
# log.debug("Putting %d items on the list..." % (number_of_items,))
|
|
||||||
if self.buffer.list.get_count() == 0:
|
|
||||||
for i in self.session.db[self.name]:
|
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
|
||||||
# self.buffer.set_list_position()
|
|
||||||
elif self.buffer.list.get_count() > 0:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
for i in self.session.db[self.name][len(self.session.db[self.name])-number_of_items:]:
|
|
||||||
tweet = self.compose_function(i, self.session.db)
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
else:
|
|
||||||
items = self.session.db[self.name][0:number_of_items]
|
|
||||||
items.reverse()
|
|
||||||
for i in items:
|
|
||||||
tweet = self.compose_function(i, self.session.db)
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
log.debug("now the list contains %d items" % (self.buffer.list.get_count(),))
|
|
||||||
|
|
||||||
def get_right_tweet(self):
|
|
||||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
|
||||||
return tweet
|
|
||||||
|
|
||||||
def add_new_item(self, item):
|
|
||||||
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
|
||||||
else:
|
|
||||||
self.buffer.list.insert_item(True, *tweet)
|
|
||||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
|
||||||
output.speak(" ".join(tweet))
|
|
||||||
|
|
||||||
def clear_list(self):
|
|
||||||
dlg = commonMessageDialogs.clear_list()
|
|
||||||
if dlg == widgetUtils.YES:
|
|
||||||
self.session.db[self.name] = []
|
|
||||||
self.session.db["cursors"][self.name] = -1
|
|
||||||
self.buffer.list.clear()
|
|
||||||
|
|
||||||
def interact(self):
|
|
||||||
user.profileController(self.session, user=self.get_right_tweet().screen_name)
|
|
||||||
|
|
||||||
def show_menu(self, ev, pos=0, *args, **kwargs):
|
|
||||||
menu = menus.peoplePanelMenu()
|
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.send_message, menuitem=menu.reply)
|
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.details, menuitem=menu.details)
|
|
||||||
# widgetUtils.connect_event(menu, widgetUtils.MENU, self.lists, menuitem=menu.lists)
|
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
|
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
|
|
||||||
if hasattr(menu, "openInBrowser"):
|
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.open_in_browser, menuitem=menu.openInBrowser)
|
|
||||||
if pos != 0:
|
|
||||||
self.buffer.PopupMenu(menu, pos)
|
|
||||||
else:
|
|
||||||
self.buffer.PopupMenu(menu, ev.GetPosition())
|
|
||||||
|
|
||||||
def details(self, *args, **kwargs):
|
|
||||||
pub.sendMessage("execute-action", action="user_details")
|
|
||||||
|
|
||||||
def auto_read(self, number_of_items):
|
|
||||||
if number_of_items == 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
tweet = self.session.db[self.name][-1]
|
|
||||||
else:
|
|
||||||
tweet = self.session.db[self.name][0]
|
|
||||||
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
|
|
||||||
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
|
||||||
output.speak(_(u"{0} new followers.").format(number_of_items))
|
|
||||||
|
|
||||||
def get_item_url(self, *args, **kwargs):
|
|
||||||
tweet = self.get_tweet()
|
|
||||||
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
|
|
||||||
return url
|
|
||||||
|
|
||||||
def view_item(self):
|
|
||||||
item_url = self.get_item_url()
|
|
||||||
non_tweet = self.get_formatted_message()
|
|
||||||
msg = messages.viewTweet(non_tweet, [], False, item_url=item_url)
|
|
@ -1,187 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import time
|
|
||||||
import locale
|
|
||||||
import widgetUtils
|
|
||||||
import logging
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
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):
|
|
||||||
last_thread_id = None
|
|
||||||
last_reply_id = None
|
|
||||||
|
|
||||||
def __init__(self, tweet, *args, **kwargs):
|
|
||||||
self.tweet = tweet
|
|
||||||
super(ConversationBuffer, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def start_stream(self, start=False, mandatory=False, play_sound=True, avoid_autoreading=False):
|
|
||||||
current_time = time.time()
|
|
||||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
|
||||||
self.execution_time = current_time
|
|
||||||
log.debug("Retrieving conversation. Last thread ID is {}, last reply ID is {}".format(self.last_thread_id, self.last_reply_id))
|
|
||||||
results = self.get_replies(self.tweet)
|
|
||||||
log.debug("Retrieved {} items before filters.".format(len(results)))
|
|
||||||
number_of_items = self.session.order_buffer(self.name, results)
|
|
||||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
|
||||||
self.put_items_on_list(number_of_items)
|
|
||||||
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
|
||||||
self.session.sound.play(self.sound)
|
|
||||||
# Autoread settings
|
|
||||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
|
||||||
self.auto_read(number_of_items)
|
|
||||||
return number_of_items
|
|
||||||
|
|
||||||
def remove_buffer(self, force=False):
|
|
||||||
if force == False:
|
|
||||||
dlg = commonMessageDialogs.remove_buffer()
|
|
||||||
else:
|
|
||||||
dlg = widgetUtils.YES
|
|
||||||
if dlg == widgetUtils.YES:
|
|
||||||
if self.name in self.session.db:
|
|
||||||
self.session.db.pop(self.name)
|
|
||||||
return True
|
|
||||||
elif dlg == widgetUtils.NO:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_replies(self, tweet):
|
|
||||||
""" Try to retrieve the whole conversation for the passed object by using a mix between calls to API V1.1 and V2 """
|
|
||||||
# firstly we would try to retrieve the whole thread, then we will get replies.
|
|
||||||
# this makes us to waste two search API calls, but there's no better option to retrieve the whole thread including replies, unfortunately.
|
|
||||||
thread_results = []
|
|
||||||
reply_results = []
|
|
||||||
# try to fetch conversation_id of the tweet initiating the buffer.
|
|
||||||
try:
|
|
||||||
tweet = self.session.twitter_v2.get_tweet(id=self.tweet.id, user_auth=True, tweet_fields=["conversation_id", "author_id"])
|
|
||||||
thread_results.append(tweet.data)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error attempting to retrieve tweet conversation ID")
|
|
||||||
thread_results.append(self.tweet)
|
|
||||||
# Return earlier cause we can't do anything if we cannot fetch the object from twitter.
|
|
||||||
return thread_results
|
|
||||||
# If tweet contains a conversation_id param, let's retrieve the original tweet which started the conversation so we will have the whole reference for later.
|
|
||||||
if hasattr(tweet.data, "conversation_id") and tweet.data.conversation_id != None:
|
|
||||||
conversation_id = tweet.data.conversation_id
|
|
||||||
original_tweet = self.session.twitter_v2.get_tweet(id=tweet.data.conversation_id, user_auth=True, tweet_fields=["conversation_id", "author_id"])
|
|
||||||
thread_results.insert(0, original_tweet.data)
|
|
||||||
else:
|
|
||||||
conversation_id = tweet.data.id
|
|
||||||
# find all tweets replying to the original thread only. Those tweets are sent by the same author who originally posted the first tweet.
|
|
||||||
try:
|
|
||||||
term = "conversation_id:{} from:{} to:{}".format(conversation_id, original_tweet.data.author_id, original_tweet.data.author_id)
|
|
||||||
thread_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=98, since_id=self.last_thread_id, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
|
|
||||||
if thread_tweets.data != None:
|
|
||||||
thread_results.extend(thread_tweets.data)
|
|
||||||
# Search only replies to conversation_id.
|
|
||||||
term = "conversation_id:{}".format(conversation_id, original_tweet.data.author_id)
|
|
||||||
reply_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=50, since_id=self.last_reply_id, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
|
|
||||||
if reply_tweets.data != None:
|
|
||||||
reply_results.extend(reply_tweets.data)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
|
|
||||||
# convert v2 tweets in normal, V1.1 tweets so we don't have to deal with those kind of objects in our infrastructure.
|
|
||||||
# ToDo: Remove this last step once we support natively all objects fetched via Twitter API V2.
|
|
||||||
results = []
|
|
||||||
ids = [tweet.id for tweet in thread_results]
|
|
||||||
if len(ids) > 0:
|
|
||||||
try:
|
|
||||||
thread_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
thread_results.sort(key=lambda x: x.id)
|
|
||||||
self.last_thread_id = thread_results[-1].id
|
|
||||||
results.extend(thread_results)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
|
||||||
return []
|
|
||||||
ids = [tweet.id for tweet in reply_results]
|
|
||||||
if len(ids) > 0:
|
|
||||||
try:
|
|
||||||
reply_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
reply_results.sort(key=lambda x: x.id)
|
|
||||||
self.last_reply_id = reply_results[-1].id
|
|
||||||
results.extend(reply_results)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
|
||||||
results.sort(key=lambda x: x.id)
|
|
||||||
return results
|
|
||||||
|
|
||||||
def get_replies_v1(self, tweet):
|
|
||||||
try:
|
|
||||||
tweet = self.session.twitter.get_status(id=tweet.id, tweet_mode="extended")
|
|
||||||
except:
|
|
||||||
log.exception("Error getting tweet for making a conversation buffer.")
|
|
||||||
return []
|
|
||||||
results = []
|
|
||||||
results.append(tweet)
|
|
||||||
if hasattr(tweet, "in_reply_to_status_id") and tweet.in_reply_to_status_id != None:
|
|
||||||
while tweet.in_reply_to_status_id != None:
|
|
||||||
original_tweet = self.session.twitter.get_status(id=tweet.in_reply_to_status_id, tweet_mode="extended")
|
|
||||||
results.insert(0, original_tweet)
|
|
||||||
tweet = original_tweet
|
|
||||||
try:
|
|
||||||
term = "from:{} to:{}".format(tweet.user.screen_name, tweet.user.screen_name)
|
|
||||||
thread_tweets = self.session.twitter.search_tweets(term, count=100, since_id=tweet.id, tweet_mode="extended")
|
|
||||||
results.extend(thread_tweets)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
|
|
||||||
|
|
||||||
try:
|
|
||||||
term = "to:{}".format(tweet.user.screen_name)
|
|
||||||
reply_tweets = self.session.twitter.search_tweets(term, count=100, since_id=tweet.id, tweet_mode="extended")
|
|
||||||
ids = [t.id for t in results]
|
|
||||||
reply_tweets = [t for t in reply_tweets if hasattr(t, "in_reply_to_status_id") and t.in_reply_to_status_id in ids]
|
|
||||||
results.extend(reply_tweets)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
|
|
||||||
results.sort(key=lambda x: x.id)
|
|
||||||
return results
|
|
@ -1,144 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import time
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
import logging
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from pubsub import pub
|
|
||||||
from wxUI import buffers, commonMessageDialogs, menus
|
|
||||||
from controller.twitter import user, messages
|
|
||||||
from controller.buffers import base
|
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.trends")
|
|
||||||
|
|
||||||
class TrendsBuffer(base.Buffer):
|
|
||||||
def __init__(self, parent, name, sessionObject, account, trendsFor, *args, **kwargs):
|
|
||||||
super(TrendsBuffer, self).__init__(parent=parent, sessionObject=sessionObject)
|
|
||||||
self.trendsFor = trendsFor
|
|
||||||
self.session = sessionObject
|
|
||||||
self.account = account
|
|
||||||
self.invisible = True
|
|
||||||
self.buffer = buffers.twitter.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 post_status(self, *args, **kwargs):
|
|
||||||
title = _("Tweet")
|
|
||||||
caption = _("Write the tweet here")
|
|
||||||
tweet = messages.tweet(self.session, title, caption, "")
|
|
||||||
response = tweet.message.ShowModal()
|
|
||||||
if response == wx.ID_OK:
|
|
||||||
tweet_data = tweet.get_tweet_data()
|
|
||||||
call_threaded(self.session.send_tweet, *tweet_data)
|
|
||||||
if hasattr(tweet.message, "destroy"):
|
|
||||||
tweet.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
|
|
||||||
try:
|
|
||||||
data = self.session.twitter.get_place_trends(id=self.trendsFor)
|
|
||||||
except TweepyException as err:
|
|
||||||
log.exception("Error %s" % (str(err)))
|
|
||||||
if not hasattr(self, "name_"):
|
|
||||||
self.name_ = data[0]["locations"][0]["name"]
|
|
||||||
pub.sendMessage("buffer-title-changed", buffer=self)
|
|
||||||
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 = _("Tweet")
|
|
||||||
caption = _("Write the tweet here")
|
|
||||||
tweet = messages.tweet(session=self.session, title=title, caption=caption, text=self.get_message()+ " ")
|
|
||||||
tweet.message.SetInsertionPoint(len(tweet.message.GetValue()))
|
|
||||||
if tweet.message.ShowModal() == widgetUtils.OK:
|
|
||||||
tweet_data = tweet.get_tweet_data()
|
|
||||||
call_threaded(self.session.send_tweet, *tweet_data)
|
|
||||||
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."))
|
|
@ -13,8 +13,6 @@ import application
|
|||||||
import sound
|
import sound
|
||||||
import output
|
import output
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from tweepy.errors import TweepyException, Forbidden
|
|
||||||
from geopy.geocoders import Nominatim
|
|
||||||
from extra import SoundsTutorial
|
from extra import SoundsTutorial
|
||||||
from update import updater
|
from update import updater
|
||||||
from wxUI import view, dialogs, commonMessageDialogs, sysTrayIcon
|
from wxUI import view, dialogs, commonMessageDialogs, sysTrayIcon
|
||||||
@ -25,14 +23,11 @@ from mysc import restart
|
|||||||
from mysc import localization
|
from mysc import localization
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from mysc.repeating_timer import RepeatingTimer
|
from mysc.repeating_timer import RepeatingTimer
|
||||||
from controller.twitter import handler as TwitterHandler
|
|
||||||
from controller.mastodon import handler as MastodonHandler
|
from controller.mastodon import handler as MastodonHandler
|
||||||
from . import settings, userAlias
|
from . import settings, userAlias
|
||||||
|
|
||||||
log = logging.getLogger("mainController")
|
log = logging.getLogger("mainController")
|
||||||
|
|
||||||
geocoder = Nominatim(user_agent="TWBlue")
|
|
||||||
|
|
||||||
class Controller(object):
|
class Controller(object):
|
||||||
|
|
||||||
""" Main Controller for TWBlue. It manages the main window and sessions."""
|
""" Main Controller for TWBlue. It manages the main window and sessions."""
|
||||||
@ -111,34 +106,17 @@ class Controller(object):
|
|||||||
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
|
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
|
||||||
pub.subscribe(self.create_account_buffer, "core.create_account")
|
pub.subscribe(self.create_account_buffer, "core.create_account")
|
||||||
|
|
||||||
# Twitter specific events.
|
|
||||||
pub.subscribe(self.buffer_title_changed, "buffer-title-changed")
|
|
||||||
pub.subscribe(self.manage_sent_dm, "twitter.sent_dm")
|
|
||||||
pub.subscribe(self.update_sent_dms, "twitter.sent_dms_updated")
|
|
||||||
pub.subscribe(self.more_dms, "twitter.more_sent_dms")
|
|
||||||
pub.subscribe(self.manage_sent_tweets, "twitter.sent_tweet")
|
|
||||||
pub.subscribe(self.manage_new_tweet, "twitter.new_tweet")
|
|
||||||
pub.subscribe(self.manage_friend, "twitter.friend")
|
|
||||||
pub.subscribe(self.manage_unfollowing, "twitter.unfollowing")
|
|
||||||
pub.subscribe(self.manage_favourite, "twitter.favourite")
|
|
||||||
pub.subscribe(self.manage_unfavourite, "twitter.unfavourite")
|
|
||||||
pub.subscribe(self.manage_blocked_user, "twitter.blocked_user")
|
|
||||||
pub.subscribe(self.manage_unblocked_user, "twitter.unblocked_user")
|
|
||||||
pub.subscribe(self.restart_streaming, "twitter.restart_streaming")
|
|
||||||
|
|
||||||
# Mastodon specific events.
|
# Mastodon specific events.
|
||||||
pub.subscribe(self.mastodon_new_item, "mastodon.new_item")
|
pub.subscribe(self.mastodon_new_item, "mastodon.new_item")
|
||||||
pub.subscribe(self.mastodon_updated_item, "mastodon.updated_item")
|
pub.subscribe(self.mastodon_updated_item, "mastodon.updated_item")
|
||||||
pub.subscribe(self.mastodon_new_conversation, "mastodon.conversation_received")
|
pub.subscribe(self.mastodon_new_conversation, "mastodon.conversation_received")
|
||||||
pub.subscribe(self.mastodon_error_post, "mastodon.error_post")
|
pub.subscribe(self.mastodon_error_post, "mastodon.error_post")
|
||||||
|
|
||||||
# connect application events to GUI
|
# connect application events to GUI
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
|
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.search, menuitem=self.view.menuitem_search)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.search, menuitem=self.view.menuitem_search)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.list_manager, menuitem=self.view.lists)
|
# widgetUtils.connect_event(self.view, widgetUtils.MENU, self.list_manager, menuitem=self.view.lists)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_trending_topics, menuitem=self.view.trends)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.filter, menuitem=self.view.filter)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_filters, menuitem=self.view.manage_filters)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.find, menuitem=self.view.find)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.find, menuitem=self.view.find)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.accountConfiguration, menuitem=self.view.account_settings)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.accountConfiguration, menuitem=self.view.account_settings)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.configuration, menuitem=self.view.prefs)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.configuration, menuitem=self.view.prefs)
|
||||||
@ -156,13 +134,10 @@ class Controller(object):
|
|||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_favourites, self.view.fav)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_favourites, self.view.fav)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_favourites, self.view.unfav)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_favourites, self.view.unfav)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_item, self.view.view)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_item, self.view.view)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_reverse_geocode, menuitem=self.view.view_coordinates)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates)
|
||||||
@ -170,8 +145,6 @@ class Controller(object):
|
|||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.visit_website, menuitem=self.view.visit_website)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.visit_website, menuitem=self.view.visit_website)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_soundpacks, menuitem=self.view.get_soundpacks)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_soundpacks, menuitem=self.view.get_soundpacks)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_accounts, self.view.manage_accounts)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_accounts, self.view.manage_accounts)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_profile, menuitem=self.view.updateProfile)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.user_details, menuitem=self.view.details)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_autoread, menuitem=self.view.autoread)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_autoread, menuitem=self.view.autoread)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_buffer_mute, self.view.mute_buffer)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_buffer_mute, self.view.mute_buffer)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_timeline, self.view.timeline)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_timeline, self.view.timeline)
|
||||||
@ -183,19 +156,16 @@ class Controller(object):
|
|||||||
widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed)
|
widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_alias, self.view.addAlias)
|
# widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_alias, self.view.addAlias)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList)
|
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_aliases, self.view.manageAliases)
|
# widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_aliases, self.view.manageAliases)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
|
||||||
|
|
||||||
def set_systray_icon(self):
|
def set_systray_icon(self):
|
||||||
self.systrayIcon = sysTrayIcon.SysTrayIcon()
|
self.systrayIcon = sysTrayIcon.SysTrayIcon()
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.post_tweet, menuitem=self.systrayIcon.tweet)
|
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.post_tweet, menuitem=self.systrayIcon.post)
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.configuration, menuitem=self.systrayIcon.global_settings)
|
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.configuration, menuitem=self.systrayIcon.global_settings)
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.accountConfiguration, menuitem=self.systrayIcon.account_settings)
|
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.accountConfiguration, menuitem=self.systrayIcon.account_settings)
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.update_profile, menuitem=self.systrayIcon.update_profile)
|
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.show_hide, menuitem=self.systrayIcon.show_hide)
|
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.show_hide, menuitem=self.systrayIcon.show_hide)
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.check_for_updates, menuitem=self.systrayIcon.check_for_updates)
|
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.check_for_updates, menuitem=self.systrayIcon.check_for_updates)
|
||||||
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.view_documentation, menuitem=self.systrayIcon.doc)
|
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.view_documentation, menuitem=self.systrayIcon.doc)
|
||||||
@ -215,9 +185,7 @@ class Controller(object):
|
|||||||
def get_handler(self, type):
|
def get_handler(self, type):
|
||||||
handler = self.handlers.get(type)
|
handler = self.handlers.get(type)
|
||||||
if handler == None:
|
if handler == None:
|
||||||
if type == "twitter":
|
if type == "mastodon":
|
||||||
handler = TwitterHandler.Handler()
|
|
||||||
elif type == "mastodon":
|
|
||||||
handler = MastodonHandler.Handler()
|
handler = MastodonHandler.Handler()
|
||||||
self.handlers[type]=handler
|
self.handlers[type]=handler
|
||||||
return handler
|
return handler
|
||||||
@ -262,9 +230,9 @@ class Controller(object):
|
|||||||
if sessions.sessions[i].is_logged == False:
|
if sessions.sessions[i].is_logged == False:
|
||||||
self.create_ignored_session_buffer(sessions.sessions[i])
|
self.create_ignored_session_buffer(sessions.sessions[i])
|
||||||
continue
|
continue
|
||||||
# Valid types currently are twitter and mastodon (Work in progress)
|
# Valid types currently are mastodon (Work in progress)
|
||||||
# More can be added later.
|
# More can be added later.
|
||||||
valid_session_types = ["twitter", "mastodon"]
|
valid_session_types = ["mastodon"]
|
||||||
if sessions.sessions[i].type in valid_session_types:
|
if sessions.sessions[i].type in valid_session_types:
|
||||||
handler = self.get_handler(type=sessions.sessions[i].type)
|
handler = self.get_handler(type=sessions.sessions[i].type)
|
||||||
handler.create_buffers(sessions.sessions[i], controller=self)
|
handler.create_buffers(sessions.sessions[i], controller=self)
|
||||||
@ -285,8 +253,6 @@ class Controller(object):
|
|||||||
if config.app["app-settings"]["speak_ready_msg"] == True:
|
if config.app["app-settings"]["speak_ready_msg"] == True:
|
||||||
output.speak(_(u"Ready"))
|
output.speak(_(u"Ready"))
|
||||||
self.started = True
|
self.started = True
|
||||||
self.streams_checker_function = RepeatingTimer(60, self.check_streams)
|
|
||||||
self.streams_checker_function.start()
|
|
||||||
if len(self.accounts) > 0:
|
if len(self.accounts) > 0:
|
||||||
b = self.get_first_buffer(self.accounts[0])
|
b = self.get_first_buffer(self.accounts[0])
|
||||||
self.update_menus(handler=self.get_handler(b.session.type))
|
self.update_menus(handler=self.get_handler(b.session.type))
|
||||||
@ -406,21 +372,6 @@ class Controller(object):
|
|||||||
return output.speak(page.get_message(), True)
|
return output.speak(page.get_message(), True)
|
||||||
output.speak(_(u"{0} not found.").format(string,), True)
|
output.speak(_(u"{0} not found.").format(string,), True)
|
||||||
|
|
||||||
def filter(self, *args, **kwargs):
|
|
||||||
buffer = self.get_current_buffer()
|
|
||||||
if not hasattr(buffer.buffer, "list"):
|
|
||||||
output.speak(_(u"No session is currently in focus. Focus a session with the next or previous session shortcut."), True)
|
|
||||||
return
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "filter"):
|
|
||||||
return handler.filter(buffer=buffer)
|
|
||||||
|
|
||||||
def manage_filters(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "manage_filters"):
|
|
||||||
return handler.manage_filters(session=buffer.session)
|
|
||||||
|
|
||||||
def seekLeft(self, *args, **kwargs):
|
def seekLeft(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
sound.URLPlayer.seek(-5000)
|
sound.URLPlayer.seek(-5000)
|
||||||
@ -457,31 +408,6 @@ class Controller(object):
|
|||||||
buffer = self.get_best_buffer()
|
buffer = self.get_best_buffer()
|
||||||
SoundsTutorial.soundsTutorial(buffer.session)
|
SoundsTutorial.soundsTutorial(buffer.session)
|
||||||
|
|
||||||
def view_user_lists(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "view_user_lists"):
|
|
||||||
return handler.view_user_lists(buffer=buffer)
|
|
||||||
|
|
||||||
def add_to_list(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "add_to_list"):
|
|
||||||
return handler.add_to_list(controller=self, buffer=buffer)
|
|
||||||
|
|
||||||
def remove_from_list(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "remove_from_list"):
|
|
||||||
return handler.remove_from_list(controller=self, buffer=buffer)
|
|
||||||
|
|
||||||
def list_manager(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "list_manager"):
|
|
||||||
lists_buffer_position = self.view.search("lists", buffer.session.get_name())
|
|
||||||
return handler.list_manager(session=buffer.session, lists_buffer_position=lists_buffer_position)
|
|
||||||
|
|
||||||
def configuration(self, *args, **kwargs):
|
def configuration(self, *args, **kwargs):
|
||||||
""" Opens the global settings dialogue."""
|
""" Opens the global settings dialogue."""
|
||||||
d = settings.globalSettingsController()
|
d = settings.globalSettingsController()
|
||||||
@ -527,10 +453,6 @@ class Controller(object):
|
|||||||
for item in sessions.sessions:
|
for item in sessions.sessions:
|
||||||
if sessions.sessions[item].logged == False:
|
if sessions.sessions[item].logged == False:
|
||||||
continue
|
continue
|
||||||
if hasattr(sessions.sessions[item], "stop_streaming"):
|
|
||||||
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()
|
sessions.sessions[item].sound.cleaner.cancel()
|
||||||
log.debug("Saving database for " + sessions.sessions[item].session_id)
|
log.debug("Saving database for " + sessions.sessions[item].session_id)
|
||||||
sessions.sessions[item].save_persistent_data()
|
sessions.sessions[item].save_persistent_data()
|
||||||
@ -538,9 +460,6 @@ class Controller(object):
|
|||||||
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
||||||
if os.path.exists(pidpath):
|
if os.path.exists(pidpath):
|
||||||
os.remove(pidpath)
|
os.remove(pidpath)
|
||||||
if hasattr(self, "streams_checker_function"):
|
|
||||||
log.debug("Stopping stream checker...")
|
|
||||||
self.streams_checker_function.cancel()
|
|
||||||
widgetUtils.exit_application()
|
widgetUtils.exit_application()
|
||||||
|
|
||||||
def follow(self, *args, **kwargs):
|
def follow(self, *args, **kwargs):
|
||||||
@ -642,19 +561,6 @@ class Controller(object):
|
|||||||
self.view.Show()
|
self.view.Show()
|
||||||
self.showing = True
|
self.showing = True
|
||||||
|
|
||||||
def get_trending_topics(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if handler != None and hasattr(handler, "get_trending_topics"):
|
|
||||||
return handler.get_trending_topics(controller=self, session=buffer.session)
|
|
||||||
|
|
||||||
def view_reverse_geocode(self, event=None):
|
|
||||||
buffer = self.get_current_buffer()
|
|
||||||
if hasattr(buffer, "reverse_geocode"):
|
|
||||||
address = buffer.reverse_geocode()
|
|
||||||
if address != None:
|
|
||||||
dlg = commonMessageDialogs.view_geodata(address[0].__str__())
|
|
||||||
|
|
||||||
def get_more_items(self, *args, **kwargs):
|
def get_more_items(self, *args, **kwargs):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
if hasattr(buffer, "get_more_items"):
|
if hasattr(buffer, "get_more_items"):
|
||||||
@ -953,70 +859,6 @@ class Controller(object):
|
|||||||
if message != None:
|
if message != None:
|
||||||
output.speak(message, speech=session.settings["reporting"]["speech_reporting"], braille=session.settings["reporting"]["braille_reporting"])
|
output.speak(message, speech=session.settings["reporting"]["speech_reporting"], braille=session.settings["reporting"]["braille_reporting"])
|
||||||
|
|
||||||
def manage_sent_dm(self, data, session_name):
|
|
||||||
buffer = self.search_buffer("sent_direct_messages", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
play_sound = "dm_sent.ogg"
|
|
||||||
if "sent_direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
|
||||||
self.notify(buffer.session, play_sound=play_sound)
|
|
||||||
buffer.add_new_item(data)
|
|
||||||
|
|
||||||
def manage_sent_tweets(self, data, session_name):
|
|
||||||
buffer = self.search_buffer("sent_tweets", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
data = buffer.session.check_quoted_status(data)
|
|
||||||
data = buffer.session.check_long_tweet(data)
|
|
||||||
if data == False: # Long tweet deleted from twishort.
|
|
||||||
return
|
|
||||||
items = buffer.session.db[buffer.name]
|
|
||||||
if buffer.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
items.append(data)
|
|
||||||
else:
|
|
||||||
items.insert(0, data)
|
|
||||||
buffer.session.db[buffer.name] = items
|
|
||||||
buffer.add_new_item(data)
|
|
||||||
|
|
||||||
def manage_friend(self, data, session_name):
|
|
||||||
buffer = self.search_buffer("friends", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
buffer.add_new_item(data)
|
|
||||||
|
|
||||||
def manage_unfollowing(self, item, session_name):
|
|
||||||
buffer = self.search_buffer("friends", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
buffer.remove_item(item)
|
|
||||||
|
|
||||||
def manage_favourite(self, data, session_name):
|
|
||||||
buffer = self.search_buffer("favourites", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
play_sound = "favourite.ogg"
|
|
||||||
if "favourites" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
|
||||||
self.notify(buffer.session, play_sound=play_sound)
|
|
||||||
buffer.add_new_item(data)
|
|
||||||
|
|
||||||
def manage_unfavourite(self, item, session_name):
|
|
||||||
buffer = self.search_buffer("favourites", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
buffer.remove_item(item)
|
|
||||||
|
|
||||||
def manage_blocked_user(self, data, session_name):
|
|
||||||
buffer = self.search_buffer("blocked", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
buffer.add_new_item(data)
|
|
||||||
|
|
||||||
def manage_unblocked_user(self, item, session_name):
|
|
||||||
buffer = self.search_buffer("blocked", session_name)
|
|
||||||
if buffer == None:
|
|
||||||
return
|
|
||||||
buffer.remove_item(item)
|
|
||||||
|
|
||||||
def start_buffers(self, session):
|
def start_buffers(self, session):
|
||||||
log.debug("starting buffers... Session %s" % (session.session_id,))
|
log.debug("starting buffers... Session %s" % (session.session_id,))
|
||||||
handler = self.get_handler(type=session.type)
|
handler = self.get_handler(type=session.type)
|
||||||
@ -1028,17 +870,6 @@ class Controller(object):
|
|||||||
for i in sessions.sessions:
|
for i in sessions.sessions:
|
||||||
self.set_buffer_positions(i)
|
self.set_buffer_positions(i)
|
||||||
|
|
||||||
def check_connection(self):
|
|
||||||
if self.started == False:
|
|
||||||
return
|
|
||||||
for i in sessions.sessions:
|
|
||||||
try:
|
|
||||||
if sessions.sessions[i].is_logged == False:
|
|
||||||
continue
|
|
||||||
sessions.sessions[i].check_connection()
|
|
||||||
except: # We shouldn't allow this function to die.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def invisible_shorcuts_changed(self, registered):
|
def invisible_shorcuts_changed(self, registered):
|
||||||
if registered == True:
|
if registered == True:
|
||||||
km = self.create_invisible_keyboard_shorcuts()
|
km = self.create_invisible_keyboard_shorcuts()
|
||||||
@ -1099,17 +930,6 @@ class Controller(object):
|
|||||||
self.accounts.remove(sessions.sessions[i].get_name())
|
self.accounts.remove(sessions.sessions[i].get_name())
|
||||||
sessions.sessions.pop(i)
|
sessions.sessions.pop(i)
|
||||||
|
|
||||||
def update_profile(self, *args, **kwargs):
|
|
||||||
buffer = self.get_best_buffer()
|
|
||||||
handler = self.get_handler(type=buffer.session.type)
|
|
||||||
if hasattr(handler, "update_profile"):
|
|
||||||
return handler.update_profile(session=buffer.session)
|
|
||||||
|
|
||||||
def user_details(self, *args, **kwargs):
|
|
||||||
buffer = self.get_current_buffer()
|
|
||||||
if hasattr(buffer, "user_details"):
|
|
||||||
buffer.user_details()
|
|
||||||
|
|
||||||
def toggle_autoread(self, *args, **kwargs):
|
def toggle_autoread(self, *args, **kwargs):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
if hasattr(buffer, "session") and buffer.session == None:
|
if hasattr(buffer, "session") and buffer.session == None:
|
||||||
@ -1174,16 +994,6 @@ class Controller(object):
|
|||||||
i.start_stream(mandatory=True)
|
i.start_stream(mandatory=True)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r." % (str(err), i.name, i.account, i.args, i.kwargs))
|
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r." % (str(err), i.name, i.account, i.args, i.kwargs))
|
||||||
# Determine if this error was caused by a block applied to the current user (IE permission errors).
|
|
||||||
if type(err) == Forbidden:
|
|
||||||
buff = self.view.search(i.name, i.account)
|
|
||||||
i.remove_buffer(force=True)
|
|
||||||
commonMessageDialogs.blocked_timeline()
|
|
||||||
if self.get_current_buffer() == i:
|
|
||||||
self.right()
|
|
||||||
self.view.delete_buffer(buff)
|
|
||||||
self.buffers.remove(i)
|
|
||||||
del i
|
|
||||||
|
|
||||||
def update_buffer(self, *args, **kwargs):
|
def update_buffer(self, *args, **kwargs):
|
||||||
bf = self.get_current_buffer()
|
bf = self.get_current_buffer()
|
||||||
@ -1198,16 +1008,12 @@ class Controller(object):
|
|||||||
def buffer_title_changed(self, buffer):
|
def buffer_title_changed(self, buffer):
|
||||||
if buffer.name.endswith("-timeline"):
|
if buffer.name.endswith("-timeline"):
|
||||||
title = _(u"Timeline for {}").format(buffer.username,)
|
title = _(u"Timeline for {}").format(buffer.username,)
|
||||||
elif buffer.name.endswith("-favorite"):
|
|
||||||
title = _(u"Likes for {}").format(buffer.username,)
|
|
||||||
elif buffer.name.endswith("-followers"):
|
elif buffer.name.endswith("-followers"):
|
||||||
title = _(u"Followers for {}").format(buffer.username,)
|
title = _(u"Followers for {}").format(buffer.username,)
|
||||||
elif buffer.name.endswith("-friends"):
|
elif buffer.name.endswith("-friends"):
|
||||||
title = _(u"Friends for {}").format(buffer.username,)
|
title = _(u"Friends for {}").format(buffer.username,)
|
||||||
elif buffer.name.endswith("-following"):
|
elif buffer.name.endswith("-following"):
|
||||||
title = _(u"Following for {}").format(buffer.username,)
|
title = _(u"Following for {}").format(buffer.username,)
|
||||||
elif buffer.name.endswith("_tt"):
|
|
||||||
title = _("Trending topics for %s") % (buffer.name_)
|
|
||||||
buffer_index = self.view.search(buffer.name, buffer.account)
|
buffer_index = self.view.search(buffer.name, buffer.account)
|
||||||
self.view.set_page_title(buffer_index, title)
|
self.view.set_page_title(buffer_index, title)
|
||||||
|
|
||||||
@ -1216,55 +1022,13 @@ class Controller(object):
|
|||||||
if hasattr(buffer, "ocr_image"):
|
if hasattr(buffer, "ocr_image"):
|
||||||
return buffer.ocr_image()
|
return buffer.ocr_image()
|
||||||
|
|
||||||
def update_sent_dms(self, total, session_name):
|
|
||||||
sent_dms = self.search_buffer("sent_direct_messages", session_name)
|
|
||||||
if sent_dms != None:
|
|
||||||
sent_dms.put_items_on_list(total)
|
|
||||||
|
|
||||||
def more_dms(self, data, account):
|
|
||||||
sent_dms = self.search_buffer("sent_direct_messages", account)
|
|
||||||
if sent_dms != None:
|
|
||||||
sent_dms.put_more_items(data)
|
|
||||||
|
|
||||||
def save_data_in_db(self):
|
def save_data_in_db(self):
|
||||||
for i in sessions.sessions:
|
for i in sessions.sessions:
|
||||||
sessions.sessions[i].save_persistent_data()
|
sessions.sessions[i].save_persistent_data()
|
||||||
|
|
||||||
def manage_new_tweet(self, data, session_name, _buffers):
|
|
||||||
sound_to_play = None
|
|
||||||
for buff in _buffers:
|
|
||||||
buffer = self.search_buffer(buff, session_name)
|
|
||||||
if buffer == None or buffer.session.get_name() != session_name:
|
|
||||||
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 toggle_share_settings(self, shareable=True):
|
def toggle_share_settings(self, shareable=True):
|
||||||
self.view.share.Enable(shareable)
|
self.view.share.Enable(shareable)
|
||||||
|
|
||||||
def check_streams(self):
|
|
||||||
if self.started == False:
|
|
||||||
return
|
|
||||||
for i in sessions.sessions:
|
|
||||||
try:
|
|
||||||
if sessions.sessions[i].is_logged == False: continue
|
|
||||||
sessions.sessions[i].check_streams()
|
|
||||||
except TweepyException: # We shouldn't allow this function to die.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def restart_streaming(self, session):
|
|
||||||
for s in sessions.sessions:
|
|
||||||
if sessions.sessions[s].session_id == session:
|
|
||||||
log.debug("Restarting stream in session %s" % (session))
|
|
||||||
sessions.sessions[s].stop_streaming()
|
|
||||||
sessions.sessions[s].start_streaming()
|
|
||||||
|
|
||||||
def mastodon_new_item(self, item, session_name, _buffers):
|
def mastodon_new_item(self, item, session_name, _buffers):
|
||||||
sound_to_play = None
|
sound_to_play = None
|
||||||
for buff in _buffers:
|
for buff in _buffers:
|
||||||
@ -1308,6 +1072,7 @@ class Controller(object):
|
|||||||
|
|
||||||
def mastodon_error_post(self, name, reply_to, visibility, posts):
|
def mastodon_error_post(self, name, reply_to, visibility, posts):
|
||||||
home = self.search_buffer("home_timeline", name)
|
home = self.search_buffer("home_timeline", name)
|
||||||
|
if home != None:
|
||||||
wx.CallAfter(home.post_from_error, visibility=visibility, data=posts)
|
wx.CallAfter(home.post_from_error, visibility=visibility, data=posts)
|
||||||
|
|
||||||
def report_error(self, *args, **kwargs):
|
def report_error(self, *args, **kwargs):
|
||||||
|
@ -3,11 +3,9 @@ import wx
|
|||||||
import logging
|
import logging
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from mysc import restart
|
from mysc import restart
|
||||||
from wxUI.dialogs.mastodon import dialogs
|
|
||||||
from wxUI.dialogs.mastodon import search as search_dialogs
|
from wxUI.dialogs.mastodon import search as search_dialogs
|
||||||
from wxUI.dialogs.mastodon import dialogs
|
from wxUI.dialogs.mastodon import dialogs
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from sessions.twitter import utils
|
|
||||||
from . import userActions, settings
|
from . import userActions, settings
|
||||||
|
|
||||||
log = logging.getLogger("controller.mastodon.handler")
|
log = logging.getLogger("controller.mastodon.handler")
|
||||||
|
@ -6,7 +6,7 @@ import widgetUtils
|
|||||||
import config
|
import config
|
||||||
import output
|
import output
|
||||||
from twitter_text import parse_tweet, config
|
from twitter_text import parse_tweet, config
|
||||||
from controller.twitter import messages
|
from controller import messages
|
||||||
from sessions.mastodon import templates
|
from sessions.mastodon import templates
|
||||||
from wxUI.dialogs.mastodon import postDialogs
|
from wxUI.dialogs.mastodon import postDialogs
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ def character_count(post_text, post_cw, character_limit=500):
|
|||||||
parsed = parse_tweet(full_text, options=options)
|
parsed = parse_tweet(full_text, options=options)
|
||||||
return parsed.weightedLength
|
return parsed.weightedLength
|
||||||
|
|
||||||
class post(messages.basicTweet):
|
class post(messages.basicMessage):
|
||||||
def __init__(self, session, title, caption, text="", *args, **kwargs):
|
def __init__(self, session, title, caption, text="", *args, **kwargs):
|
||||||
# take max character limit from session as this might be different for some instances.
|
# take max character limit from session as this might be different for some instances.
|
||||||
self.max = session.char_limit
|
self.max = session.char_limit
|
||||||
|
@ -9,7 +9,7 @@ import output
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from wxUI.dialogs.mastodon import configuration
|
from wxUI.dialogs.mastodon import configuration
|
||||||
from extra.autocompletionUsers import scan, manage
|
#from extra.autocompletionUsers import scan, manage
|
||||||
from extra.ocr import OCRSpace
|
from extra.ocr import OCRSpace
|
||||||
from controller.settings import globalSettingsController
|
from controller.settings import globalSettingsController
|
||||||
from . templateEditor import EditTemplate
|
from . templateEditor import EditTemplate
|
||||||
|
@ -3,7 +3,7 @@ import re
|
|||||||
import wx
|
import wx
|
||||||
from typing import List
|
from typing import List
|
||||||
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables
|
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables
|
||||||
from wxUI.dialogs.twitterDialogs import templateDialogs
|
from wxUI.dialogs import templateDialogs
|
||||||
|
|
||||||
class EditTemplate(object):
|
class EditTemplate(object):
|
||||||
def __init__(self, template: str, type: str) -> None:
|
def __init__(self, template: str, type: str) -> None:
|
||||||
|
@ -6,7 +6,7 @@ from wxUI.dialogs.mastodon import userActions as userActionsDialog
|
|||||||
from wxUI.dialogs.mastodon import userTimeline as userTimelineDialog
|
from wxUI.dialogs.mastodon import userTimeline as userTimelineDialog
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from mastodon import MastodonError, MastodonNotFoundError
|
from mastodon import MastodonError, MastodonNotFoundError
|
||||||
from extra.autocompletionUsers import completion
|
#from extra.autocompletionUsers import completion
|
||||||
|
|
||||||
log = logging.getLogger("controller.mastodon.userActions")
|
log = logging.getLogger("controller.mastodon.userActions")
|
||||||
|
|
||||||
|
41
src/controller/messages.py
Normal file
41
src/controller/messages.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import widgetUtils
|
||||||
|
import output
|
||||||
|
from extra import translator, SpellChecker
|
||||||
|
|
||||||
|
class basicMessage(object):
|
||||||
|
def translate(self, event=None):
|
||||||
|
dlg = translator.gui.translateDialog()
|
||||||
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
|
text_to_translate = self.message.text.GetValue()
|
||||||
|
language_dict = translator.translator.available_languages()
|
||||||
|
for k in language_dict:
|
||||||
|
if language_dict[k] == dlg.dest_lang.GetStringSelection():
|
||||||
|
dst = k
|
||||||
|
msg = translator.translator.translate(text=text_to_translate, target=dst)
|
||||||
|
self.message.text.ChangeValue(msg)
|
||||||
|
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||||
|
self.text_processor()
|
||||||
|
self.message.text.SetFocus()
|
||||||
|
output.speak(_(u"Translated"))
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
def text_processor(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def spellcheck(self, event=None):
|
||||||
|
text = self.message.text.GetValue()
|
||||||
|
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
||||||
|
if hasattr(checker, "fixed_text"):
|
||||||
|
self.message.text.ChangeValue(checker.fixed_text)
|
||||||
|
self.text_processor()
|
||||||
|
self.message.text.SetFocus()
|
||||||
|
|
||||||
|
def remove_attachment(self, *args, **kwargs):
|
||||||
|
attachment = self.message.attachments.GetFocusedItem()
|
||||||
|
if attachment > -1 and len(self.attachments) > attachment:
|
||||||
|
self.attachments.pop(attachment)
|
||||||
|
self.message.remove_item(list_type="attachment")
|
||||||
|
self.text_processor()
|
||||||
|
self.message.text.SetFocus()
|
@ -1 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
@ -1,76 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import time
|
|
||||||
import widgetUtils
|
|
||||||
import application
|
|
||||||
from wxUI.dialogs import filterDialogs
|
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
|
|
||||||
class filter(object):
|
|
||||||
def __init__(self, buffer, filter_title=None, if_word_exists=None, in_lang=None, regexp=None, word=None, in_buffer=None):
|
|
||||||
self.buffer = buffer
|
|
||||||
self.dialog = filterDialogs.filterDialog(languages=[i["name"] for i in application.supported_languages])
|
|
||||||
if self.dialog.get_response() == widgetUtils.OK:
|
|
||||||
title = self.dialog.get("title")
|
|
||||||
contains = self.dialog.get("contains")
|
|
||||||
term = self.dialog.get("term")
|
|
||||||
regexp = self.dialog.get("regexp")
|
|
||||||
allow_rts = self.dialog.get("allow_rts")
|
|
||||||
allow_quotes = self.dialog.get("allow_quotes")
|
|
||||||
allow_replies = self.dialog.get("allow_replies")
|
|
||||||
load_language = self.dialog.get("load_language")
|
|
||||||
ignore_language = self.dialog.get("ignore_language")
|
|
||||||
lang_option = None
|
|
||||||
if ignore_language:
|
|
||||||
lang_option = False
|
|
||||||
elif load_language:
|
|
||||||
lang_option = True
|
|
||||||
langs = self.dialog.get_selected_langs()
|
|
||||||
langcodes = []
|
|
||||||
for i in application.supported_languages:
|
|
||||||
if i["name"] in langs:
|
|
||||||
langcodes.append(i["code"])
|
|
||||||
d = dict(in_buffer=self.buffer.name, word=term, regexp=regexp, in_lang=lang_option, languages=langcodes, if_word_exists=contains, allow_rts=allow_rts, allow_quotes=allow_quotes, allow_replies=allow_replies)
|
|
||||||
if title in self.buffer.session.settings["filters"]:
|
|
||||||
return commonMessageDialogs.existing_filter()
|
|
||||||
self.buffer.session.settings["filters"][title] = d
|
|
||||||
self.buffer.session.settings.write()
|
|
||||||
|
|
||||||
class filterManager(object):
|
|
||||||
|
|
||||||
def __init__(self, session):
|
|
||||||
self.session = session
|
|
||||||
self.dialog = filterDialogs.filterManagerDialog()
|
|
||||||
self.insert_filters(self.session.settings["filters"])
|
|
||||||
if self.dialog.filters.get_count() == 0:
|
|
||||||
self.dialog.edit.Enable(False)
|
|
||||||
self.dialog.delete.Enable(False)
|
|
||||||
else:
|
|
||||||
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_filter)
|
|
||||||
widgetUtils.connect_event(self.dialog.delete, widgetUtils.BUTTON_PRESSED, self.delete_filter)
|
|
||||||
response = self.dialog.get_response()
|
|
||||||
|
|
||||||
def insert_filters(self, filters):
|
|
||||||
self.dialog.filters.clear()
|
|
||||||
for f in list(filters.keys()):
|
|
||||||
filterName = f
|
|
||||||
buffer = filters[f]["in_buffer"]
|
|
||||||
if filters[f]["if_word_exists"] == "True" and filters[f]["word"] != "":
|
|
||||||
filter_by_word = "True"
|
|
||||||
else:
|
|
||||||
filter_by_word = "False"
|
|
||||||
filter_by_lang = ""
|
|
||||||
if filters[f]["in_lang"] != "None":
|
|
||||||
filter_by_lang = "True"
|
|
||||||
b = [f, buffer, filter_by_word, filter_by_lang]
|
|
||||||
self.dialog.filters.insert_item(False, *b)
|
|
||||||
|
|
||||||
def edit_filter(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_filter(self, *args, **kwargs):
|
|
||||||
filter_title = self.dialog.filters.get_text_column(self.dialog.filters.get_selected(), 0)
|
|
||||||
response = commonMessageDialogs.delete_filter()
|
|
||||||
if response == widgetUtils.YES:
|
|
||||||
self.session.settings["filters"].pop(filter_title)
|
|
||||||
self.session.settings.write()
|
|
||||||
self.insert_filters(self.session.settings["filters"])
|
|
@ -1,377 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import logging
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from pubsub import pub
|
|
||||||
from tweepy.errors import TweepyException, Forbidden
|
|
||||||
from mysc import restart
|
|
||||||
from sessions.twitter import utils, compose
|
|
||||||
from controller import userSelector
|
|
||||||
from wxUI import dialogs, commonMessageDialogs
|
|
||||||
from . import filters, lists, settings, userActions, trendingTopics, user
|
|
||||||
|
|
||||||
log = logging.getLogger("controller.twitter.handler")
|
|
||||||
|
|
||||||
class Handler(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Handler, self).__init__()
|
|
||||||
# Structure to hold names for menu bar items.
|
|
||||||
# empty names mean the item will be Disabled.
|
|
||||||
self.menus = dict(
|
|
||||||
# In application menu.
|
|
||||||
updateProfile=_("&Update profile"),
|
|
||||||
menuitem_search=_("&Search"),
|
|
||||||
lists=_("&Lists manager"),
|
|
||||||
manageAliases=_("Manage user aliases"),
|
|
||||||
# In Item Menu.
|
|
||||||
compose=_("&Tweet"),
|
|
||||||
reply=_("Re&ply"),
|
|
||||||
share=_("&Retweet"),
|
|
||||||
fav=_("&Like"),
|
|
||||||
unfav=_("&Unlike"),
|
|
||||||
view=_("&Show tweet"),
|
|
||||||
view_coordinates=_("View &address"),
|
|
||||||
view_conversation=_("View conversa&tion"),
|
|
||||||
ocr=_("Read text in picture"),
|
|
||||||
delete=_("&Delete"),
|
|
||||||
# In user menu.
|
|
||||||
follow=_("&Actions..."),
|
|
||||||
timeline=_("&View timeline..."),
|
|
||||||
dm=_("Direct me&ssage"),
|
|
||||||
addAlias=_("Add a&lias"),
|
|
||||||
addToList=_("&Add to list"),
|
|
||||||
removeFromList=_("R&emove from list"),
|
|
||||||
viewLists=_("&View lists"),
|
|
||||||
details=_("Show user &profile"),
|
|
||||||
favs=_("View likes"),
|
|
||||||
# In buffer menu.
|
|
||||||
trends=_("New &trending topics buffer..."),
|
|
||||||
filter=_("Create a &filter"),
|
|
||||||
manage_filters=_("&Manage filters"),
|
|
||||||
)
|
|
||||||
# Name for the "tweet" menu in the menu bar.
|
|
||||||
self.item_menu = _("&Tweet")
|
|
||||||
|
|
||||||
def create_buffers(self, session, createAccounts=True, controller=None):
|
|
||||||
session.get_user_info()
|
|
||||||
name = session.get_name()
|
|
||||||
controller.accounts.append(name)
|
|
||||||
if createAccounts == True:
|
|
||||||
pub.sendMessage("core.create_account", name=name, session_id=session.session_id, logged=True)
|
|
||||||
root_position =controller.view.search(name, name)
|
|
||||||
for i in session.settings['general']['buffer_order']:
|
|
||||||
if i == 'home':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Home"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="home_timeline", name="home_timeline", sessionObject=session, account=session.get_name(), sound="tweet_received.ogg", include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
elif i == 'mentions':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="mentions_timeline", name="mentions", sessionObject=session, account=session.get_name(), sound="mention_received.ogg", include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
elif i == 'dm':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="DirectMessagesBuffer", session_type=session.type, buffer_title=_("Direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_direct_messages", name="direct_messages", sessionObject=session, account=session.get_name(), bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg"))
|
|
||||||
elif i == 'sent_dm':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="SentDirectMessagesBuffer", session_type=session.type, buffer_title=_("Sent direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function=None, name="sent_direct_messages", sessionObject=session, account=session.get_name(), bufferType="dmPanel", compose_func="compose_direct_message"))
|
|
||||||
elif i == 'sent_tweets':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent tweets"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="user_timeline", name="sent_tweets", sessionObject=session, account=session.get_name(), screen_name=session.db["user_name"], include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
elif i == 'favorites':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_favorites", name="favourites", sessionObject=session, account=session.get_name(), sound="favourite.ogg", include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
elif i == 'followers':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_followers", name="followers", sessionObject=session, account=session.get_name(), sound="update_followers.ogg", screen_name=session.db["user_name"]))
|
|
||||||
elif i == 'friends':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Following"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_friends", name="friends", sessionObject=session, account=session.get_name(), screen_name=session.db["user_name"]))
|
|
||||||
elif i == 'blocks':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_blocks", name="blocked", sessionObject=session, account=session.get_name()))
|
|
||||||
elif i == 'muted':
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Muted users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_mutes", name="muted", sessionObject=session, account=session.get_name()))
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="timelines", account=name))
|
|
||||||
timelines_position =controller.view.search("timelines", name)
|
|
||||||
for i in session.settings["other_buffers"]["timelines"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_(u"Timeline for {}").format(i,), parent_tab=timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="user_timeline", name="%s-timeline" % (i,), sessionObject=session, account=session.get_name(), sound="tweet_timeline.ogg", bufferType=None, user_id=i, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Likes timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="favs_timelines", account=name))
|
|
||||||
favs_timelines_position =controller.view.search("favs_timelines", name)
|
|
||||||
for i in session.settings["other_buffers"]["favourites_timelines"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes for {}").format(i,), parent_tab=favs_timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_favorites", name="%s-favorite" % (i,), sessionObject=session, account=name, bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Followers timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="followers_timelines", account=session.get_name()))
|
|
||||||
followers_timelines_position =controller.view.search("followers_timelines", name)
|
|
||||||
for i in session.settings["other_buffers"]["followers_timelines"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers for {}").format(i,), parent_tab=followers_timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_followers", name="%s-followers" % (i,), sessionObject=session, account=session.get_name(), sound="new_event.ogg", user_id=i))
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Following timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="friends_timelines", account=name))
|
|
||||||
friends_timelines_position =controller.view.search("friends_timelines", name)
|
|
||||||
for i in session.settings["other_buffers"]["friends_timelines"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_(u"Friends for {}").format(i,), parent_tab=friends_timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_friends", name="%s-friends" % (i,), sessionObject=session, account=session.get_name(), sound="new_event.ogg", user_id=i))
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Lists"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="lists", account=name))
|
|
||||||
lists_position =controller.view.search("lists", name)
|
|
||||||
for i in session.settings["other_buffers"]["lists"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=controller.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, account=session.get_name(), bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Searches"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="searches", account=name))
|
|
||||||
searches_position =controller.view.search("searches", name)
|
|
||||||
for i in session.settings["other_buffers"]["tweet_searches"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_(u"Search for {}").format(i), parent_tab=searches_position, start=False, kwargs=dict(parent=controller.view.nb, function="search_tweets", name="%s-searchterm" % (i,), sessionObject=session, account=session.get_name(), bufferType="searchPanel", sound="search_updated.ogg", q=i, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (i,), sessionObject=session, account=session.get_name(), trendsFor=i, sound="trends_updated.ogg"))
|
|
||||||
|
|
||||||
def filter(self, buffer):
|
|
||||||
# Let's prevent filtering of some buffers (people buffers, direct messages, events and sent items).
|
|
||||||
if (buffer.name == "direct_messages" or buffer.name == "sent_tweets") or buffer.type == "people":
|
|
||||||
output.speak(_("Filters cannot be applied on this buffer"))
|
|
||||||
return
|
|
||||||
new_filter = filters.filter(buffer)
|
|
||||||
|
|
||||||
def manage_filters(self, session):
|
|
||||||
manage_filters = filters.filterManager(session)
|
|
||||||
|
|
||||||
def view_user_lists(self, buffer):
|
|
||||||
if not hasattr(buffer, "get_right_tweet"):
|
|
||||||
return
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if buffer.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
elif buffer.type == "dm":
|
|
||||||
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, buffer.session)
|
|
||||||
selector = userSelector.userSelector(users=users, session_id=buffer.session.session_id)
|
|
||||||
user = selector.get_user()
|
|
||||||
if user == None:
|
|
||||||
return
|
|
||||||
l = lists.listsController(buffer.session, user=user)
|
|
||||||
|
|
||||||
def add_to_list(self, controller, buffer):
|
|
||||||
if not hasattr(buffer, "get_right_tweet"):
|
|
||||||
return
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if buffer.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
elif buffer.type == "dm":
|
|
||||||
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, buffer.session)
|
|
||||||
selector = userSelector.userSelector(users=users, session_id=buffer.session.session_id)
|
|
||||||
user = selector.get_user()
|
|
||||||
if user == None:
|
|
||||||
return
|
|
||||||
dlg = dialogs.lists.addUserListDialog()
|
|
||||||
dlg.populate_list([compose.compose_list(item) for item in buffer.session.db["lists"]])
|
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
|
||||||
try:
|
|
||||||
list = buffer.session.twitter.add_list_member(list_id=buffer.session.db["lists"][dlg.get_item()].id, screen_name=user)
|
|
||||||
older_list = utils.find_item(buffer.session.db["lists"][dlg.get_item()].id, buffer.session.db["lists"])
|
|
||||||
listBuffer = controller.search_buffer("%s-list" % (buffer.session.db["lists"][dlg.get_item()].name.lower()), buff.session.get_name())
|
|
||||||
if listBuffer != None:
|
|
||||||
listBuffer.get_user_ids()
|
|
||||||
buffer.session.db["lists"].pop(older_list)
|
|
||||||
buffer.session.db["lists"].append(list)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
|
|
||||||
def remove_from_list(self, controller, buffer):
|
|
||||||
if not hasattr(buffer, "get_right_tweet"):
|
|
||||||
return
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if buffer.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
elif buffer.type == "dm":
|
|
||||||
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, buffer.session)
|
|
||||||
selector = userSelector.userSelector(users=users, session_id=buffer.session.session_id)
|
|
||||||
user = selector.get_user()
|
|
||||||
if user == None:
|
|
||||||
return
|
|
||||||
dlg = dialogs.lists.removeUserListDialog()
|
|
||||||
dlg.populate_list([compose.compose_list(item) for item in buffer.session.db["lists"]])
|
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
|
||||||
try:
|
|
||||||
list = buffer.session.twitter.remove_list_member(list_id=buffer.session.db["lists"][dlg.get_item()].id, screen_name=user)
|
|
||||||
older_list = utils.find_item(buffer.session.db["lists"][dlg.get_item()].id, buffer.session.db["lists"])
|
|
||||||
listBuffer = controller.search_buffer("%s-list" % (buffer.session.db["lists"][dlg.get_item()].name.lower()), buffer.session.get_name())
|
|
||||||
if listBuffer != None:
|
|
||||||
listBuffer.get_user_ids()
|
|
||||||
buffer.session.db["lists"].pop(older_list)
|
|
||||||
buffer.session.db["lists"].append(list)
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
||||||
|
|
||||||
def list_manager(self, session, lists_buffer_position):
|
|
||||||
return lists.listsController(session=session, lists_buffer_position=lists_buffer_position)
|
|
||||||
|
|
||||||
def account_settings(self, buffer, controller):
|
|
||||||
d = settings.accountSettingsController(buffer, controller)
|
|
||||||
if d.response == widgetUtils.OK:
|
|
||||||
d.save_configuration()
|
|
||||||
if d.needs_restart == True:
|
|
||||||
commonMessageDialogs.needs_restart()
|
|
||||||
buffer.session.settings.write()
|
|
||||||
buffer.session.save_persistent_data()
|
|
||||||
restart.restart_program()
|
|
||||||
|
|
||||||
def follow(self, buffer):
|
|
||||||
if not hasattr(buffer, "get_right_tweet"):
|
|
||||||
return
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if buffer.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
elif buffer.type == "dm":
|
|
||||||
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, buffer.session)
|
|
||||||
u = userActions.userActionsController(buffer, users)
|
|
||||||
|
|
||||||
def add_alias(self, buffer):
|
|
||||||
if not hasattr(buffer, "get_right_tweet"):
|
|
||||||
return
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if buffer.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
elif buffer.type == "dm":
|
|
||||||
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, buffer.session)
|
|
||||||
dlg = dialogs.userAliasDialogs.addAliasDialog(_("Add an user alias"), users)
|
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
|
||||||
user, alias = dlg.get_user()
|
|
||||||
if user == "" or alias == "":
|
|
||||||
return
|
|
||||||
user_id = buffer.session.get_user_by_screen_name(user)
|
|
||||||
buffer.session.settings["user-aliases"][str(user_id)] = alias
|
|
||||||
buffer.session.settings.write()
|
|
||||||
output.speak(_("Alias has been set correctly for {}.").format(user))
|
|
||||||
pub.sendMessage("alias-added")
|
|
||||||
|
|
||||||
# ToDo: explore how to play sound & save config differently.
|
|
||||||
# currently, TWBlue will play the sound and save the config for the timeline even if the buffer did not load or something else.
|
|
||||||
def open_timeline(self, controller, buffer, default="tweets"):
|
|
||||||
if not hasattr(buffer, "get_right_tweet"):
|
|
||||||
return
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if buffer.type == "people":
|
|
||||||
users = [tweet.screen_name]
|
|
||||||
elif buffer.type == "dm":
|
|
||||||
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
|
||||||
else:
|
|
||||||
users = utils.get_all_users(tweet, buffer.session)
|
|
||||||
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
|
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
|
||||||
usr = utils.if_user_exists(buffer.session.twitter, dlg.get_user())
|
|
||||||
if usr != None:
|
|
||||||
if usr == dlg.get_user():
|
|
||||||
commonMessageDialogs.suspended_user()
|
|
||||||
return
|
|
||||||
if usr.protected == True:
|
|
||||||
if usr.following == False:
|
|
||||||
commonMessageDialogs.no_following()
|
|
||||||
return
|
|
||||||
tl_type = dlg.get_action()
|
|
||||||
if tl_type == "tweets":
|
|
||||||
if usr.statuses_count == 0:
|
|
||||||
commonMessageDialogs.no_tweets()
|
|
||||||
return
|
|
||||||
if usr.id_str in buffer.session.settings["other_buffers"]["timelines"]:
|
|
||||||
commonMessageDialogs.timeline_exist()
|
|
||||||
return
|
|
||||||
timelines_position =controller.view.search("timelines", buffer.session.get_name())
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=buffer.session.type, buffer_title=_("Timeline for {}").format(usr.screen_name,), parent_tab=timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="user_timeline", name="%s-timeline" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="tweet_timeline.ogg", bufferType=None, user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
buffer.session.settings["other_buffers"]["timelines"].append(usr.id_str)
|
|
||||||
buffer.session.sound.play("create_timeline.ogg")
|
|
||||||
elif tl_type == "favourites":
|
|
||||||
if usr.favourites_count == 0:
|
|
||||||
commonMessageDialogs.no_favs()
|
|
||||||
return
|
|
||||||
if usr.id_str in buffer.session.settings["other_buffers"]["favourites_timelines"]:
|
|
||||||
commonMessageDialogs.timeline_exist()
|
|
||||||
return
|
|
||||||
favs_timelines_position =controller.view.search("favs_timelines", buffer.session.get_name())
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=buffer.session.type, buffer_title=_("Likes for {}").format(usr.screen_name,), parent_tab=favs_timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="get_favorites", name="%s-favorite" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
buffer.session.settings["other_buffers"]["favourites_timelines"].append(usr.id_str)
|
|
||||||
buffer.session.sound.play("create_timeline.ogg")
|
|
||||||
elif tl_type == "followers":
|
|
||||||
if usr.followers_count == 0:
|
|
||||||
commonMessageDialogs.no_followers()
|
|
||||||
return
|
|
||||||
if usr.id_str in buffer.session.settings["other_buffers"]["followers_timelines"]:
|
|
||||||
commonMessageDialogs.timeline_exist()
|
|
||||||
return
|
|
||||||
followers_timelines_position =controller.view.search("followers_timelines", buffer.session.get_name())
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=buffer.session.type, buffer_title=_("Followers for {}").format(usr.screen_name,), parent_tab=followers_timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="get_followers", name="%s-followers" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="new_event.ogg", user_id=usr.id_str))
|
|
||||||
buffer.session.settings["other_buffers"]["followers_timelines"].append(usr.id_str)
|
|
||||||
buffer.session.sound.play("create_timeline.ogg")
|
|
||||||
elif tl_type == "friends":
|
|
||||||
if usr.friends_count == 0:
|
|
||||||
commonMessageDialogs.no_friends()
|
|
||||||
return
|
|
||||||
if usr.id_str in buffer.session.settings["other_buffers"]["friends_timelines"]:
|
|
||||||
commonMessageDialogs.timeline_exist()
|
|
||||||
return
|
|
||||||
friends_timelines_position =controller.view.search("friends_timelines", buffer.session.get_name())
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=buffer.session.type, buffer_title=_("Friends for {}").format(usr.screen_name,), parent_tab=friends_timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="get_friends", name="%s-friends" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="new_event.ogg", user_id=usr.id_str))
|
|
||||||
buffer.session.settings["other_buffers"]["friends_timelines"].append(usr.id_str)
|
|
||||||
buffer.session.sound.play("create_timeline.ogg")
|
|
||||||
else:
|
|
||||||
commonMessageDialogs.user_not_exist()
|
|
||||||
buffer.session.settings.write()
|
|
||||||
|
|
||||||
def open_conversation(self, controller, buffer):
|
|
||||||
tweet = buffer.get_right_tweet()
|
|
||||||
if hasattr(tweet, "retweeted_status") and tweet.retweeted_status != None:
|
|
||||||
tweet = tweet.retweeted_status
|
|
||||||
user = buffer.session.get_user(tweet.user).screen_name
|
|
||||||
searches_position =controller.view.search("searches", buffer.session.get_name())
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="ConversationBuffer", session_type=buffer.session.type, buffer_title=_(u"Conversation with {0}").format(user), parent_tab=searches_position, start=True, kwargs=dict(tweet=tweet, parent=controller.view.nb, function="search_tweets", name="%s-searchterm" % (tweet.id,), sessionObject=buffer.session, account=buffer.session.get_name(), bufferType="searchPanel", sound="search_updated.ogg", since_id=tweet.id, q="@{0}".format(user)))
|
|
||||||
|
|
||||||
def get_trending_topics(self, controller, session):
|
|
||||||
trends = trendingTopics.trendingTopicsController(session)
|
|
||||||
if trends.dialog.get_response() == widgetUtils.OK:
|
|
||||||
woeid = trends.get_woeid()
|
|
||||||
if woeid in session.settings["other_buffers"]["trending_topic_buffers"]:
|
|
||||||
return
|
|
||||||
root_position =controller.view.search(session.get_name(), session.get_name())
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (trends.get_string()), parent_tab=root_position, start=True, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (woeid,), sessionObject=session, account=session.get_name(), trendsFor=woeid, sound="trends_updated.ogg"))
|
|
||||||
session.settings["other_buffers"]["trending_topic_buffers"].append(str(woeid))
|
|
||||||
session.settings.write()
|
|
||||||
|
|
||||||
def start_buffer(self, controller, buffer):
|
|
||||||
if hasattr(buffer, "finished_timeline") and buffer.finished_timeline == False:
|
|
||||||
change_title = True
|
|
||||||
else:
|
|
||||||
change_title = False
|
|
||||||
try:
|
|
||||||
if "mentions" in buffer.name or "direct_messages" in buffer.name:
|
|
||||||
buffer.start_stream()
|
|
||||||
else:
|
|
||||||
buffer.start_stream(play_sound=False)
|
|
||||||
except TweepyException as err:
|
|
||||||
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r." % (str(err), buffer.name, buffer.account, buffer.args, buffer.kwargs))
|
|
||||||
# Determine if this error was caused by a block applied to the current user (IE permission errors).
|
|
||||||
if type(err) == Forbidden:
|
|
||||||
buff = controller.view.search(buffer.name, buffer.account)
|
|
||||||
buffer.remove_buffer(force=True)
|
|
||||||
commonMessageDialogs.blocked_timeline()
|
|
||||||
if controller.get_current_buffer() == buffer:
|
|
||||||
controller.right()
|
|
||||||
controller.view.delete_buffer(buff)
|
|
||||||
controller.buffers.remove(buffer)
|
|
||||||
del buffer
|
|
||||||
if change_title:
|
|
||||||
pub.sendMessage("buffer-title-changed", buffer=buffer)
|
|
||||||
|
|
||||||
def update_profile(self, session):
|
|
||||||
r = user.profileController(session)
|
|
||||||
|
|
||||||
def search(self, controller, session, value):
|
|
||||||
log.debug("Creating a new search...")
|
|
||||||
dlg = dialogs.search.searchDialog(value)
|
|
||||||
if dlg.get_response() == widgetUtils.OK and dlg.get("term") != "":
|
|
||||||
term = dlg.get("term")
|
|
||||||
searches_position =controller.view.search("searches", session.get_name())
|
|
||||||
if dlg.get("tweets") == True:
|
|
||||||
if term not in session.settings["other_buffers"]["tweet_searches"]:
|
|
||||||
session.settings["other_buffers"]["tweet_searches"].append(term)
|
|
||||||
session.settings.write()
|
|
||||||
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=controller.view.nb, function="search_tweets", name="%s-searchterm" % (term,), sessionObject=session, account=session.get_name(), bufferType="searchPanel", sound="search_updated.ogg", q=term, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
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:
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="SearchPeopleBuffer", session_type=session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=controller.view.nb, function="search_users", name="%s-searchUser" % (term,), sessionObject=session, account=session.get_name(), bufferType=None, sound="search_updated.ogg", q=term))
|
|
||||||
dlg.Destroy()
|
|
@ -1,117 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
import logging
|
|
||||||
from wxUI.dialogs import lists
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from sessions.twitter import compose, utils
|
|
||||||
from pubsub import pub
|
|
||||||
|
|
||||||
log = logging.getLogger("controller.listsController")
|
|
||||||
|
|
||||||
class listsController(object):
|
|
||||||
def __init__(self, session, user=None, lists_buffer_position=0):
|
|
||||||
super(listsController, self).__init__()
|
|
||||||
self.session = session
|
|
||||||
self.lists_buffer_position = lists_buffer_position
|
|
||||||
if user == None:
|
|
||||||
self.dialog = lists.listViewer()
|
|
||||||
self.dialog.populate_list(self.get_all_lists())
|
|
||||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
|
|
||||||
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
|
|
||||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
|
|
||||||
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
|
|
||||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
|
|
||||||
else:
|
|
||||||
self.dialog = lists.userListViewer(user)
|
|
||||||
self.dialog.populate_list(self.get_user_lists(user))
|
|
||||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
|
|
||||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
|
|
||||||
self.dialog.get_response()
|
|
||||||
|
|
||||||
def get_all_lists(self):
|
|
||||||
return [compose.compose_list(item) for item in self.session.db["lists"]]
|
|
||||||
|
|
||||||
def get_user_lists(self, user):
|
|
||||||
self.lists = self.session.twitter.get_lists(reverse=True, screen_name=user)
|
|
||||||
return [compose.compose_list(item) for item in self.lists]
|
|
||||||
|
|
||||||
def create_list(self, *args, **kwargs):
|
|
||||||
dialog = lists.createListDialog()
|
|
||||||
if dialog.get_response() == widgetUtils.OK:
|
|
||||||
name = dialog.get("name")
|
|
||||||
description = dialog.get("description")
|
|
||||||
p = dialog.get("public")
|
|
||||||
if p == True:
|
|
||||||
mode = "public"
|
|
||||||
else:
|
|
||||||
mode = "private"
|
|
||||||
try:
|
|
||||||
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
|
|
||||||
self.session.db["lists"].append(new_list)
|
|
||||||
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
||||||
dialog.destroy()
|
|
||||||
|
|
||||||
def edit_list(self, *args, **kwargs):
|
|
||||||
if self.dialog.lista.get_count() == 0: return
|
|
||||||
list = self.session.db["lists"][self.dialog.get_item()]
|
|
||||||
dialog = lists.editListDialog(list)
|
|
||||||
if dialog.get_response() == widgetUtils.OK:
|
|
||||||
name = dialog.get("name")
|
|
||||||
description = dialog.get("description")
|
|
||||||
p = dialog.get("public")
|
|
||||||
if p == True:
|
|
||||||
mode = "public"
|
|
||||||
else:
|
|
||||||
mode = "private"
|
|
||||||
try:
|
|
||||||
self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode)
|
|
||||||
self.session.get_lists()
|
|
||||||
self.dialog.populate_list(self.get_all_lists(), True)
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
||||||
dialog.destroy()
|
|
||||||
|
|
||||||
def remove_list(self, *args, **kwargs):
|
|
||||||
if self.dialog.lista.get_count() == 0: return
|
|
||||||
list = self.session.db["lists"][self.dialog.get_item()].id
|
|
||||||
if lists.remove_list() == widgetUtils.YES:
|
|
||||||
try:
|
|
||||||
self.session.twitter.destroy_list(list_id=list)
|
|
||||||
self.session.db["lists"].pop(self.dialog.get_item())
|
|
||||||
self.dialog.lista.remove_item(self.dialog.get_item())
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
||||||
|
|
||||||
def open_list_as_buffer(self, *args, **kwargs):
|
|
||||||
if self.dialog.lista.get_count() == 0: return
|
|
||||||
list = self.session.db["lists"][self.dialog.get_item()]
|
|
||||||
pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=self.session.type, buffer_title=_("List for {}").format(list.name), parent_tab=self.lists_buffer_position, start=True, kwargs=dict(function="list_timeline", name="%s-list" % (list.name,), sessionObject=self.session, account=self.session.get_name(), bufferType=None, sound="list_tweet.ogg", list_id=list.id, include_ext_alt_text=True, tweet_mode="extended"))
|
|
||||||
self.session.settings["other_buffers"]["lists"].append(list.name)
|
|
||||||
self.session.settings.write()
|
|
||||||
|
|
||||||
def subscribe(self, *args, **kwargs):
|
|
||||||
if self.dialog.lista.get_count() == 0: return
|
|
||||||
list_id = self.lists[self.dialog.get_item()].id
|
|
||||||
try:
|
|
||||||
list = self.session.twitter.subscribe_list(list_id=list_id)
|
|
||||||
item = utils.find_item(list.id, self.session.db["lists"])
|
|
||||||
self.session.db["lists"].append(list)
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
||||||
|
|
||||||
def unsubscribe(self, *args, **kwargs):
|
|
||||||
if self.dialog.lista.get_count() == 0: return
|
|
||||||
list_id = self.lists[self.dialog.get_item()].id
|
|
||||||
try:
|
|
||||||
list = self.session.twitter.unsubscribe_list(list_id=list_id)
|
|
||||||
self.session.db["lists"].remove(list)
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak("error %s" % (str(e)))
|
|
||||||
log.exception("error %s" % (str(e)))
|
|
@ -1,382 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
import arrow
|
|
||||||
import languageHandler
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
import sound
|
|
||||||
import config
|
|
||||||
from pubsub import pub
|
|
||||||
from twitter_text.parse_tweet import parse_tweet
|
|
||||||
from wxUI.dialogs import twitterDialogs, urlList
|
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
from extra import translator, SpellChecker
|
|
||||||
from extra.AudioUploader import audioUploader
|
|
||||||
from extra.autocompletionUsers import completion
|
|
||||||
from sessions.twitter import utils
|
|
||||||
|
|
||||||
class basicTweet(object):
|
|
||||||
""" This class handles the tweet main features. Other classes should derive from this class."""
|
|
||||||
def __init__(self, session, title, caption, text="", messageType="tweet", max=280, *args, **kwargs):
|
|
||||||
super(basicTweet, self).__init__()
|
|
||||||
self.max = max
|
|
||||||
self.title = title
|
|
||||||
self.session = session
|
|
||||||
self.message = getattr(twitterDialogs, messageType)(title=title, caption=caption, message=text, max_length=max, *args, **kwargs)
|
|
||||||
self.message.text.SetValue(text)
|
|
||||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
|
||||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
|
||||||
widgetUtils.connect_event(self.message.add_audio, widgetUtils.BUTTON_PRESSED, self.attach)
|
|
||||||
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
|
||||||
widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate)
|
|
||||||
if hasattr(self.message, "add"):
|
|
||||||
widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach)
|
|
||||||
self.attachments = []
|
|
||||||
|
|
||||||
def translate(self, event=None):
|
|
||||||
dlg = translator.gui.translateDialog()
|
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
|
||||||
text_to_translate = self.message.text.GetValue()
|
|
||||||
language_dict = translator.translator.available_languages()
|
|
||||||
for k in language_dict:
|
|
||||||
if language_dict[k] == dlg.dest_lang.GetStringSelection():
|
|
||||||
dst = k
|
|
||||||
msg = translator.translator.translate(text=text_to_translate, target=dst)
|
|
||||||
self.message.text.ChangeValue(msg)
|
|
||||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text.SetFocus()
|
|
||||||
output.speak(_(u"Translated"))
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
|
||||||
text = self.message.text.GetValue()
|
|
||||||
results = parse_tweet(text)
|
|
||||||
self.message.SetTitle(_("%s - %s of %d characters") % (self.title, results.weightedLength, self.max))
|
|
||||||
if results.weightedLength > self.max:
|
|
||||||
self.session.sound.play("max_length.ogg")
|
|
||||||
|
|
||||||
def spellcheck(self, event=None):
|
|
||||||
text = self.message.text.GetValue()
|
|
||||||
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
|
||||||
if hasattr(checker, "fixed_text"):
|
|
||||||
self.message.text.ChangeValue(checker.fixed_text)
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text.SetFocus()
|
|
||||||
|
|
||||||
def attach(self, *args, **kwargs):
|
|
||||||
def completed_callback(dlg):
|
|
||||||
url = dlg.uploaderFunction.get_url()
|
|
||||||
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
|
|
||||||
dlg.uploaderDialog.destroy()
|
|
||||||
if "sndup.net/" in url:
|
|
||||||
self.message.text.ChangeValue(self.message.text.GetValue()+url+" #audio")
|
|
||||||
self.text_processor()
|
|
||||||
else:
|
|
||||||
commonMessageDialogs.common_error(url)
|
|
||||||
dlg.cleanup()
|
|
||||||
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
|
|
||||||
self.message.text.SetFocus()
|
|
||||||
|
|
||||||
def can_attach(self):
|
|
||||||
if len(self.attachments) == 0:
|
|
||||||
return True
|
|
||||||
elif len(self.attachments) == 1 and (self.attachments[0]["type"] == "video" or self.attachments[0]["type"] == "gif"):
|
|
||||||
return False
|
|
||||||
elif len(self.attachments) < 4:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def on_attach(self, *args, **kwargs):
|
|
||||||
can_attach = self.can_attach()
|
|
||||||
menu = self.message.attach_menu(can_attach)
|
|
||||||
self.message.Bind(wx.EVT_MENU, self.on_attach_image, self.message.add_image)
|
|
||||||
self.message.Bind(wx.EVT_MENU, self.on_attach_video, self.message.add_video)
|
|
||||||
if hasattr(self.message, "add_poll"):
|
|
||||||
self.message.Bind(wx.EVT_MENU, self.on_attach_poll, self.message.add_poll)
|
|
||||||
self.message.PopupMenu(menu, self.message.add.GetPosition())
|
|
||||||
|
|
||||||
def on_attach_image(self, *args, **kwargs):
|
|
||||||
can_attach = self.can_attach()
|
|
||||||
video_or_gif_present = False
|
|
||||||
for a in self.attachments:
|
|
||||||
if a["type"] == "video" or a["type"] == "gif":
|
|
||||||
video_or_gif = True
|
|
||||||
break
|
|
||||||
if can_attach == False or video_or_gif_present == True:
|
|
||||||
return self.message.unable_to_attach_file()
|
|
||||||
image, description = self.message.get_image()
|
|
||||||
if image != None:
|
|
||||||
if image.endswith("gif"):
|
|
||||||
image_type = "gif"
|
|
||||||
else:
|
|
||||||
image_type = "photo"
|
|
||||||
imageInfo = {"type": image_type, "file": image, "description": description}
|
|
||||||
if len(self.attachments) > 0 and image_type == "gif":
|
|
||||||
return self.message.unable_to_attach_file()
|
|
||||||
self.attachments.append(imageInfo)
|
|
||||||
self.message.add_item(item=[os.path.basename(imageInfo["file"]), imageInfo["type"], imageInfo["description"]])
|
|
||||||
self.text_processor()
|
|
||||||
|
|
||||||
def on_attach_video(self, *args, **kwargs):
|
|
||||||
if len(self.attachments) > 0:
|
|
||||||
return self.message.unable_to_attach_file()
|
|
||||||
video = self.message.get_video()
|
|
||||||
if video != None:
|
|
||||||
videoInfo = {"type": "video", "file": video, "description": ""}
|
|
||||||
if len(self.attachments) > 0:
|
|
||||||
return self.message.unable_to_attach_file()
|
|
||||||
self.attachments.append(videoInfo)
|
|
||||||
self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]])
|
|
||||||
self.text_processor()
|
|
||||||
|
|
||||||
def on_attach_poll(self, *args, **kwargs):
|
|
||||||
dlg = twitterDialogs.poll()
|
|
||||||
if dlg.ShowModal() == wx.ID_OK:
|
|
||||||
self.poll_options = dlg.get_options()
|
|
||||||
self.poll_period = 60*24*dlg.period.GetValue()
|
|
||||||
dlg.Destroy()
|
|
||||||
|
|
||||||
def remove_attachment(self, *args, **kwargs):
|
|
||||||
attachment = self.message.attachments.GetFocusedItem()
|
|
||||||
if attachment > -1 and len(self.attachments) > attachment:
|
|
||||||
self.attachments.pop(attachment)
|
|
||||||
self.message.remove_item(list_type="attachment")
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text.SetFocus()
|
|
||||||
|
|
||||||
class tweet(basicTweet):
|
|
||||||
def __init__(self, session, title, caption, text="", max=280, messageType="tweet", *args, **kwargs):
|
|
||||||
self.thread = []
|
|
||||||
self.poll_options = None
|
|
||||||
self.poll_period = None
|
|
||||||
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
|
|
||||||
widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
|
||||||
if hasattr(self.message, "add_tweet"):
|
|
||||||
widgetUtils.connect_event(self.message.add_tweet, widgetUtils.BUTTON_PRESSED, self.add_tweet)
|
|
||||||
widgetUtils.connect_event(self.message.remove_tweet, widgetUtils.BUTTON_PRESSED, self.remove_tweet)
|
|
||||||
widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
|
|
||||||
self.text_processor()
|
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
|
||||||
c = completion.autocompletionUsers(self.message, self.session.session_id)
|
|
||||||
c.show_menu()
|
|
||||||
|
|
||||||
def add_tweet(self, event, update_gui=True, *args, **kwargs):
|
|
||||||
text = self.message.text.GetValue()
|
|
||||||
attachments = self.attachments[::]
|
|
||||||
tweetdata = dict(text=text, attachments=attachments, poll_options=self.poll_options, poll_period=self.poll_period)
|
|
||||||
self.thread.append(tweetdata)
|
|
||||||
self.attachments = []
|
|
||||||
self.poll_options = None
|
|
||||||
self.poll_period = None
|
|
||||||
if update_gui:
|
|
||||||
self.message.reset_controls()
|
|
||||||
self.message.add_item(item=[text, len(attachments)], list_type="tweet")
|
|
||||||
self.message.text.SetFocus()
|
|
||||||
self.text_processor()
|
|
||||||
|
|
||||||
def get_tweet_data(self):
|
|
||||||
self.add_tweet(event=None, update_gui=False)
|
|
||||||
return self.thread
|
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
|
||||||
super(tweet, self).text_processor(*args, **kwargs)
|
|
||||||
if len(self.thread) > 0:
|
|
||||||
if hasattr(self.message, "tweets"):
|
|
||||||
self.message.tweets.Enable(True)
|
|
||||||
self.message.remove_tweet.Enable(True)
|
|
||||||
else:
|
|
||||||
self.message.tweets.Enable(False)
|
|
||||||
self.message.remove_tweet.Enable(False)
|
|
||||||
if len(self.attachments) > 0:
|
|
||||||
self.message.attachments.Enable(True)
|
|
||||||
self.message.remove_attachment.Enable(True)
|
|
||||||
else:
|
|
||||||
self.message.attachments.Enable(False)
|
|
||||||
self.message.remove_attachment.Enable(False)
|
|
||||||
if hasattr(self.message, "add_tweet"):
|
|
||||||
if len(self.message.text.GetValue()) > 0 or len(self.attachments) > 0:
|
|
||||||
self.message.add_tweet.Enable(True)
|
|
||||||
else:
|
|
||||||
self.message.add_tweet.Enable(False)
|
|
||||||
|
|
||||||
def remove_tweet(self, *args, **kwargs):
|
|
||||||
tweet = self.message.tweets.GetFocusedItem()
|
|
||||||
if tweet > -1 and len(self.thread) > tweet:
|
|
||||||
self.thread.pop(tweet)
|
|
||||||
self.message.remove_item(list_type="tweet")
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text.SetFocus()
|
|
||||||
|
|
||||||
|
|
||||||
class reply(tweet):
|
|
||||||
def __init__(self, session, title, caption, text, users=[], ids=[]):
|
|
||||||
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
|
|
||||||
self.ids = ids
|
|
||||||
self.users = users
|
|
||||||
if len(users) > 0:
|
|
||||||
widgetUtils.connect_event(self.message.mention_all, widgetUtils.CHECKBOX, self.mention_all)
|
|
||||||
self.message.mention_all.Enable(True)
|
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
|
||||||
self.message.mention_all.SetValue(config.app["app-settings"]["mention_all"])
|
|
||||||
self.mention_all()
|
|
||||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
|
||||||
self.text_processor()
|
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
|
||||||
super(tweet, self).text_processor(*args, **kwargs)
|
|
||||||
if len(self.attachments) > 0:
|
|
||||||
self.message.attachments.Enable(True)
|
|
||||||
self.message.remove_attachment.Enable(True)
|
|
||||||
else:
|
|
||||||
self.message.attachments.Enable(False)
|
|
||||||
self.message.remove_attachment.Enable(False)
|
|
||||||
|
|
||||||
def mention_all(self, *args, **kwargs):
|
|
||||||
if self.message.mention_all.GetValue() == True:
|
|
||||||
for i in self.message.checkboxes:
|
|
||||||
i.SetValue(True)
|
|
||||||
i.Hide()
|
|
||||||
else:
|
|
||||||
for i in self.message.checkboxes:
|
|
||||||
i.SetValue(False)
|
|
||||||
i.Show()
|
|
||||||
|
|
||||||
def get_ids(self):
|
|
||||||
excluded_ids = []
|
|
||||||
for i in range(0, len(self.message.checkboxes)):
|
|
||||||
if self.message.checkboxes[i].GetValue() == False:
|
|
||||||
excluded_ids.append(self.ids[i])
|
|
||||||
return excluded_ids
|
|
||||||
|
|
||||||
def get_people(self):
|
|
||||||
people = ""
|
|
||||||
for i in range(0, len(self.message.checkboxes)):
|
|
||||||
if self.message.checkboxes[i].GetValue() == True:
|
|
||||||
people = people + "{0} ".format(self.message.checkboxes[i].GetLabel(),)
|
|
||||||
return people
|
|
||||||
|
|
||||||
class dm(basicTweet):
|
|
||||||
def __init__(self, session, title, caption, users):
|
|
||||||
super(dm, self).__init__(session, title, caption, messageType="dm", max=10000, users=users)
|
|
||||||
widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
|
||||||
self.text_processor()
|
|
||||||
widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed)
|
|
||||||
|
|
||||||
def user_changed(self, *args, **kwargs):
|
|
||||||
self.title = _("Direct message to %s") % (self.message.cb.GetValue())
|
|
||||||
self.text_processor()
|
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
|
||||||
c = completion.autocompletionUsers(self.message, self.session.session_id)
|
|
||||||
c.show_menu("dm")
|
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
|
||||||
super(dm, self).text_processor(*args, **kwargs)
|
|
||||||
if len(self.attachments) > 0:
|
|
||||||
self.message.attachments.Enable(True)
|
|
||||||
self.message.remove_attachment.Enable(True)
|
|
||||||
else:
|
|
||||||
self.message.attachments.Enable(False)
|
|
||||||
self.message.remove_attachment.Enable(False)
|
|
||||||
|
|
||||||
def can_attach(self):
|
|
||||||
if len(self.attachments) == 0:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
class viewTweet(basicTweet):
|
|
||||||
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""):
|
|
||||||
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
|
|
||||||
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
|
|
||||||
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
|
|
||||||
param is_tweet: True or false, depending wether the passed object is a tweet or not."""
|
|
||||||
if is_tweet == True:
|
|
||||||
self.title = _(u"Tweet")
|
|
||||||
image_description = []
|
|
||||||
text = ""
|
|
||||||
for i in range(0, len(tweetList)):
|
|
||||||
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
|
|
||||||
if hasattr(tweetList[i], "message") and tweetList[i].is_quote_status == False:
|
|
||||||
value = "message"
|
|
||||||
else:
|
|
||||||
value = "full_text"
|
|
||||||
if hasattr(tweetList[i], "retweeted_status") and tweetList[i].is_quote_status == False:
|
|
||||||
if not hasattr(tweetList[i], "message"):
|
|
||||||
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, tweetList[i].retweeted_status.full_text)
|
|
||||||
else:
|
|
||||||
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, getattr(tweetList[i], value))
|
|
||||||
else:
|
|
||||||
text = text + " @%s: %s\n" % (tweetList[i].user.screen_name, getattr(tweetList[i], value))
|
|
||||||
# tweets with extended_entities could include image descriptions.
|
|
||||||
if hasattr(tweetList[i], "extended_entities") and "media" in tweetList[i].extended_entities:
|
|
||||||
for z in tweetList[i].extended_entities["media"]:
|
|
||||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
|
||||||
image_description.append(z["ext_alt_text"])
|
|
||||||
if hasattr(tweetList[i], "retweeted_status") and hasattr(tweetList[i].retweeted_status, "extended_entities") and "media" in tweetList[i].retweeted_status["extended_entities"]:
|
|
||||||
for z in tweetList[i].retweeted_status.extended_entities["media"]:
|
|
||||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
|
||||||
image_description.append(z["ext_alt_text"])
|
|
||||||
# set rt and likes counters.
|
|
||||||
rt_count = str(tweet.retweet_count)
|
|
||||||
favs_count = str(tweet.favorite_count)
|
|
||||||
# Gets the client from where this tweet was made.
|
|
||||||
source = tweet.source
|
|
||||||
original_date = arrow.get(tweet.created_at, locale="en")
|
|
||||||
date = original_date.shift(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
|
|
||||||
if text == "":
|
|
||||||
if hasattr(tweet, "message"):
|
|
||||||
value = "message"
|
|
||||||
else:
|
|
||||||
value = "full_text"
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
if not hasattr(tweet, "message"):
|
|
||||||
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, tweet.retweeted_status.full_text)
|
|
||||||
else:
|
|
||||||
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, getattr(tweet, value))
|
|
||||||
else:
|
|
||||||
text = getattr(tweet, value)
|
|
||||||
text = self.clear_text(text)
|
|
||||||
if hasattr(tweet, "extended_entities") and "media" in tweet.extended_entities:
|
|
||||||
for z in tweet.extended_entities["media"]:
|
|
||||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
|
||||||
image_description.append(z["ext_alt_text"])
|
|
||||||
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities") and "media" in tweet.retweeted_status.extended_entities:
|
|
||||||
for z in tweet.retweeted_status.extended_entities["media"]:
|
|
||||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
|
||||||
image_description.append(z["ext_alt_text"])
|
|
||||||
self.message = twitterDialogs.viewTweet(text, rt_count, favs_count, source, date)
|
|
||||||
results = parse_tweet(text)
|
|
||||||
self.message.set_title(results.weightedLength)
|
|
||||||
[self.message.set_image_description(i) for i in image_description]
|
|
||||||
else:
|
|
||||||
self.title = _(u"View item")
|
|
||||||
text = tweet
|
|
||||||
self.message = twitterDialogs.viewNonTweet(text, date)
|
|
||||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
|
||||||
if item_url != "":
|
|
||||||
self.message.enable_button("share")
|
|
||||||
widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share)
|
|
||||||
self.item_url = item_url
|
|
||||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
|
||||||
self.message.ShowModal()
|
|
||||||
|
|
||||||
# We won't need text_processor in this dialog, so let's avoid it.
|
|
||||||
def text_processor(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def clear_text(self, text):
|
|
||||||
text = utils.StripChars(text)
|
|
||||||
urls = utils.find_urls_in_text(text)
|
|
||||||
for i in urls:
|
|
||||||
if "https://twitter.com/" in i:
|
|
||||||
text = text.replace(i, "\n")
|
|
||||||
return text
|
|
||||||
|
|
||||||
def share(self, *args, **kwargs):
|
|
||||||
if hasattr(self, "item_url"):
|
|
||||||
output.copy(self.item_url)
|
|
||||||
output.speak(_("Link copied to clipboard."))
|
|
@ -1,247 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import logging
|
|
||||||
import sound_lib
|
|
||||||
import paths
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from collections import OrderedDict
|
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
from extra.autocompletionUsers import scan, manage
|
|
||||||
from extra.ocr import OCRSpace
|
|
||||||
from controller.settings import globalSettingsController
|
|
||||||
from . templateEditor import EditTemplate
|
|
||||||
|
|
||||||
log = logging.getLogger("Settings")
|
|
||||||
|
|
||||||
class accountSettingsController(globalSettingsController):
|
|
||||||
def __init__(self, buffer, window):
|
|
||||||
self.user = buffer.session.db["user_name"]
|
|
||||||
self.buffer = buffer
|
|
||||||
self.window = window
|
|
||||||
self.config = buffer.session.settings
|
|
||||||
super(accountSettingsController, self).__init__()
|
|
||||||
|
|
||||||
def create_config(self):
|
|
||||||
self.dialog.create_general_account()
|
|
||||||
widgetUtils.connect_event(self.dialog.general.userAutocompletionScan, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_scan)
|
|
||||||
widgetUtils.connect_event(self.dialog.general.userAutocompletionManage, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_manage)
|
|
||||||
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
|
|
||||||
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
|
|
||||||
self.dialog.set_value("general", "hide_emojis", self.config["general"]["hide_emojis"])
|
|
||||||
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
|
|
||||||
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
|
|
||||||
rt = self.config["general"]["retweet_mode"]
|
|
||||||
if rt == "ask":
|
|
||||||
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
|
|
||||||
elif rt == "direct":
|
|
||||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
|
|
||||||
else:
|
|
||||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
|
|
||||||
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
|
|
||||||
self.dialog.set_value("general", "load_cache_in_memory", self.config["general"]["load_cache_in_memory"])
|
|
||||||
self.dialog.create_reporting()
|
|
||||||
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"])
|
|
||||||
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
|
|
||||||
tweet_template = self.config["templates"]["tweet"]
|
|
||||||
dm_template = self.config["templates"]["dm"]
|
|
||||||
sent_dm_template = self.config["templates"]["dm_sent"]
|
|
||||||
person_template = self.config["templates"]["person"]
|
|
||||||
self.dialog.create_templates(tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template)
|
|
||||||
widgetUtils.connect_event(self.dialog.templates.tweet, widgetUtils.BUTTON_PRESSED, self.edit_tweet_template)
|
|
||||||
widgetUtils.connect_event(self.dialog.templates.dm, widgetUtils.BUTTON_PRESSED, self.edit_dm_template)
|
|
||||||
widgetUtils.connect_event(self.dialog.templates.sent_dm, widgetUtils.BUTTON_PRESSED, self.edit_sent_dm_template)
|
|
||||||
widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template)
|
|
||||||
self.dialog.create_other_buffers()
|
|
||||||
buffer_values = self.get_buffers_list()
|
|
||||||
self.dialog.buffers.insert_buffers(buffer_values)
|
|
||||||
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
|
|
||||||
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
|
|
||||||
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
|
|
||||||
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
|
|
||||||
|
|
||||||
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
|
|
||||||
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
|
|
||||||
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
|
|
||||||
self.input_devices = sound_lib.input.Input.get_device_names()
|
|
||||||
self.output_devices = sound_lib.output.Output.get_device_names()
|
|
||||||
self.soundpacks = []
|
|
||||||
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(os.path.join(paths.sound_path(), i)) == True ]
|
|
||||||
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
|
|
||||||
self.dialog.set_value("sound", "volumeCtrl", int(self.config["sound"]["volume"]*100))
|
|
||||||
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
|
|
||||||
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
|
|
||||||
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
|
|
||||||
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
|
|
||||||
self.dialog.set_value("sound", "indicate_audio", self.config["sound"]["indicate_audio"])
|
|
||||||
self.dialog.set_value("sound", "indicate_geo", self.config["sound"]["indicate_geo"])
|
|
||||||
self.dialog.set_value("sound", "indicate_img", self.config["sound"]["indicate_img"])
|
|
||||||
self.dialog.create_extras(OCRSpace.translatable_langs)
|
|
||||||
self.dialog.set_value("extras", "sndup_apiKey", self.config["sound"]["sndup_api_key"])
|
|
||||||
language_index = OCRSpace.OcrLangs.index(self.config["mysc"]["ocr_language"])
|
|
||||||
self.dialog.extras.ocr_lang.SetSelection(language_index)
|
|
||||||
self.dialog.realize()
|
|
||||||
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
|
|
||||||
self.response = self.dialog.get_response()
|
|
||||||
|
|
||||||
def edit_tweet_template(self, *args, **kwargs):
|
|
||||||
template = self.config["templates"]["tweet"]
|
|
||||||
control = EditTemplate(template=template, type="tweet")
|
|
||||||
result = control.run_dialog()
|
|
||||||
if result != "": # Template has been saved.
|
|
||||||
self.config["templates"]["tweet"] = result
|
|
||||||
self.config.write()
|
|
||||||
self.dialog.templates.tweet.SetLabel(_("Edit template for tweets. Current template: {}").format(result))
|
|
||||||
|
|
||||||
def edit_dm_template(self, *args, **kwargs):
|
|
||||||
template = self.config["templates"]["dm"]
|
|
||||||
control = EditTemplate(template=template, type="dm")
|
|
||||||
result = control.run_dialog()
|
|
||||||
if result != "": # Template has been saved.
|
|
||||||
self.config["templates"]["dm"] = result
|
|
||||||
self.config.write()
|
|
||||||
self.dialog.templates.dm.SetLabel(_("Edit template for direct messages. Current template: {}").format(result))
|
|
||||||
|
|
||||||
def edit_sent_dm_template(self, *args, **kwargs):
|
|
||||||
template = self.config["templates"]["dm_sent"]
|
|
||||||
control = EditTemplate(template=template, type="dm")
|
|
||||||
result = control.run_dialog()
|
|
||||||
if result != "": # Template has been saved.
|
|
||||||
self.config["templates"]["dm_sent"] = result
|
|
||||||
self.config.write()
|
|
||||||
self.dialog.templates.sent_dm.SetLabel(_("Edit template for sent direct messages. Current template: {}").format(result))
|
|
||||||
|
|
||||||
def edit_person_template(self, *args, **kwargs):
|
|
||||||
template = self.config["templates"]["person"]
|
|
||||||
control = EditTemplate(template=template, type="person")
|
|
||||||
result = control.run_dialog()
|
|
||||||
if result != "": # Template has been saved.
|
|
||||||
self.config["templates"]["person"] = result
|
|
||||||
self.config.write()
|
|
||||||
self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result))
|
|
||||||
|
|
||||||
def save_configuration(self):
|
|
||||||
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
|
||||||
self.needs_restart = True
|
|
||||||
log.debug("Triggered app restart due to change in relative times.")
|
|
||||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
|
||||||
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
|
|
||||||
self.config["general"]["hide_emojis"] = self.dialog.get_value("general", "hide_emojis")
|
|
||||||
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
|
|
||||||
if self.config["general"]["load_cache_in_memory"] != self.dialog.get_value("general", "load_cache_in_memory"):
|
|
||||||
self.config["general"]["load_cache_in_memory"] = self.dialog.get_value("general", "load_cache_in_memory")
|
|
||||||
self.needs_restart = True
|
|
||||||
log.debug("Triggered app restart due to change in database strategy management.")
|
|
||||||
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
|
||||||
if self.dialog.get_value("general", "persist_size") == '':
|
|
||||||
self.config["general"]["persist_size"] =-1
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
|
|
||||||
except ValueError:
|
|
||||||
output.speak("Invalid cache size, setting to default.",True)
|
|
||||||
self.config["general"]["persist_size"] =1764
|
|
||||||
|
|
||||||
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
|
|
||||||
self.needs_restart = True
|
|
||||||
log.debug("Triggered app restart due to change in timeline order.")
|
|
||||||
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
|
|
||||||
rt = self.dialog.get_value("general", "retweet_mode")
|
|
||||||
if rt == _(u"Ask"):
|
|
||||||
self.config["general"]["retweet_mode"] = "ask"
|
|
||||||
elif rt == _(u"Retweet without comments"):
|
|
||||||
self.config["general"]["retweet_mode"] = "direct"
|
|
||||||
else:
|
|
||||||
self.config["general"]["retweet_mode"] = "comment"
|
|
||||||
buffers_list = self.dialog.buffers.get_list()
|
|
||||||
if buffers_list != self.config["general"]["buffer_order"]:
|
|
||||||
self.needs_restart = True
|
|
||||||
log.debug("Triggered app restart due to change in buffer ordering.")
|
|
||||||
self.config["general"]["buffer_order"] = buffers_list
|
|
||||||
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
|
|
||||||
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
|
|
||||||
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
|
|
||||||
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
|
|
||||||
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
|
|
||||||
try:
|
|
||||||
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
|
|
||||||
except:
|
|
||||||
self.config["sound"]["input_device"] = "default"
|
|
||||||
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
|
|
||||||
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
|
|
||||||
try:
|
|
||||||
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
|
|
||||||
except:
|
|
||||||
self.config["sound"]["output_device"] = "default"
|
|
||||||
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
|
|
||||||
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
|
|
||||||
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
|
|
||||||
self.config["sound"]["indicate_audio"] = self.dialog.get_value("sound", "indicate_audio")
|
|
||||||
self.config["sound"]["indicate_geo"] = self.dialog.get_value("sound", "indicate_geo")
|
|
||||||
self.config["sound"]["indicate_img"] = self.dialog.get_value("sound", "indicate_img")
|
|
||||||
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("extras", "sndup_apiKey")
|
|
||||||
self.buffer.session.sound.config = self.config["sound"]
|
|
||||||
self.buffer.session.sound.check_soundpack()
|
|
||||||
self.config.write()
|
|
||||||
|
|
||||||
def toggle_state(self,*args,**kwargs):
|
|
||||||
return self.dialog.buffers.change_selected_item()
|
|
||||||
|
|
||||||
def on_autocompletion_scan(self, *args, **kwargs):
|
|
||||||
configuration = scan.autocompletionScan(self.buffer.session.settings, self.buffer, self.window)
|
|
||||||
to_scan = configuration.show_dialog()
|
|
||||||
if to_scan == True:
|
|
||||||
configuration.prepare_progress_dialog()
|
|
||||||
t = threading.Thread(target=configuration.scan)
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_autocompletion_manage(self, *args, **kwargs):
|
|
||||||
configuration = manage.autocompletionManage(self.buffer.session)
|
|
||||||
configuration.show_settings()
|
|
||||||
|
|
||||||
def add_ignored_client(self, *args, **kwargs):
|
|
||||||
client = commonMessageDialogs.get_ignored_client()
|
|
||||||
if client == None: return
|
|
||||||
if client not in self.config["twitter"]["ignored_clients"]:
|
|
||||||
self.config["twitter"]["ignored_clients"].append(client)
|
|
||||||
self.dialog.ignored_clients.append(client)
|
|
||||||
|
|
||||||
def remove_ignored_client(self, *args, **kwargs):
|
|
||||||
if self.dialog.ignored_clients.get_clients() == 0: return
|
|
||||||
id = self.dialog.ignored_clients.get_client_id()
|
|
||||||
self.config["twitter"]["ignored_clients"].pop(id)
|
|
||||||
self.dialog.ignored_clients.remove_(id)
|
|
||||||
|
|
||||||
def get_buffers_list(self):
|
|
||||||
all_buffers=OrderedDict()
|
|
||||||
all_buffers['home']=_(u"Home")
|
|
||||||
all_buffers['mentions']=_(u"Mentions")
|
|
||||||
all_buffers['dm']=_(u"Direct Messages")
|
|
||||||
all_buffers['sent_dm']=_(u"Sent direct messages")
|
|
||||||
all_buffers['sent_tweets']=_(u"Sent tweets")
|
|
||||||
all_buffers['favorites']=_(u"Likes")
|
|
||||||
all_buffers['followers']=_(u"Followers")
|
|
||||||
all_buffers['friends']=_(u"Friends")
|
|
||||||
all_buffers['blocks']=_(u"Blocked users")
|
|
||||||
all_buffers['muted']=_(u"Muted users")
|
|
||||||
list_buffers = []
|
|
||||||
hidden_buffers=[]
|
|
||||||
all_buffers_keys = list(all_buffers.keys())
|
|
||||||
# Check buffers shown first.
|
|
||||||
for i in self.config["general"]["buffer_order"]:
|
|
||||||
if i in all_buffers_keys:
|
|
||||||
list_buffers.append((i, all_buffers[i], True))
|
|
||||||
# This second pass will retrieve all hidden buffers.
|
|
||||||
for i in all_buffers_keys:
|
|
||||||
if i not in self.config["general"]["buffer_order"]:
|
|
||||||
hidden_buffers.append((i, all_buffers[i], False))
|
|
||||||
list_buffers.extend(hidden_buffers)
|
|
||||||
return list_buffers
|
|
||||||
|
|
||||||
def toggle_buffer_active(self, ev):
|
|
||||||
change = self.dialog.buffers.get_event(ev)
|
|
||||||
if change == True:
|
|
||||||
self.dialog.buffers.change_selected_item()
|
|
@ -1,40 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import re
|
|
||||||
import wx
|
|
||||||
from typing import List
|
|
||||||
from sessions.twitter.templates import tweet_variables, dm_variables, person_variables
|
|
||||||
from wxUI.dialogs.twitterDialogs import templateDialogs
|
|
||||||
|
|
||||||
class EditTemplate(object):
|
|
||||||
def __init__(self, template: str, type: str) -> None:
|
|
||||||
super(EditTemplate, self).__init__()
|
|
||||||
self.default_template = template
|
|
||||||
if type == "tweet":
|
|
||||||
self.variables = tweet_variables
|
|
||||||
elif type == "dm":
|
|
||||||
self.variables = dm_variables
|
|
||||||
else:
|
|
||||||
self.variables = person_variables
|
|
||||||
self.template: str = template
|
|
||||||
|
|
||||||
def validate_template(self, template: str) -> bool:
|
|
||||||
used_variables: List[str] = re.findall("\$\w+", template)
|
|
||||||
validated: bool = True
|
|
||||||
for var in used_variables:
|
|
||||||
if var[1:] not in self.variables:
|
|
||||||
validated = False
|
|
||||||
return validated
|
|
||||||
|
|
||||||
def run_dialog(self) -> str:
|
|
||||||
dialog = templateDialogs.EditTemplateDialog(template=self.template, variables=self.variables, default_template=self.default_template)
|
|
||||||
response = dialog.ShowModal()
|
|
||||||
if response == wx.ID_SAVE:
|
|
||||||
validated: bool = self.validate_template(dialog.template.GetValue())
|
|
||||||
if validated == False:
|
|
||||||
templateDialogs.invalid_template()
|
|
||||||
self.template = dialog.template.GetValue()
|
|
||||||
return self.run_dialog()
|
|
||||||
else:
|
|
||||||
return dialog.template.GetValue()
|
|
||||||
else:
|
|
||||||
return ""
|
|
@ -1,45 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from wxUI.dialogs import trends
|
|
||||||
import widgetUtils
|
|
||||||
|
|
||||||
class trendingTopicsController(object):
|
|
||||||
def __init__(self, session):
|
|
||||||
super(trendingTopicsController, self).__init__()
|
|
||||||
self.countries = {}
|
|
||||||
self.cities = {}
|
|
||||||
self.dialog = trends.trendingTopicsDialog()
|
|
||||||
self.information = session.twitter.available_trends()
|
|
||||||
self.split_information()
|
|
||||||
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
|
|
||||||
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
|
|
||||||
self.get_places()
|
|
||||||
|
|
||||||
def split_information(self):
|
|
||||||
for i in self.information:
|
|
||||||
if i["placeType"]["name"] == "Country":
|
|
||||||
self.countries[i["name"]] = i["woeid"]
|
|
||||||
else:
|
|
||||||
self.cities[i["name"]] = i["woeid"]
|
|
||||||
|
|
||||||
def get_places(self, event=None):
|
|
||||||
values = []
|
|
||||||
if self.dialog.get_active() == "country":
|
|
||||||
for i in self.information:
|
|
||||||
if i["placeType"]["name"] == "Country":
|
|
||||||
values.append(i["name"])
|
|
||||||
elif self.dialog.get_active() == "city":
|
|
||||||
for i in self.information:
|
|
||||||
if i["placeType"]["name"] != "Country":
|
|
||||||
values.append(i["name"])
|
|
||||||
self.dialog.set(values)
|
|
||||||
|
|
||||||
def get_woeid(self):
|
|
||||||
selected = self.dialog.get_item()
|
|
||||||
if self.dialog.get_active() == "country":
|
|
||||||
woeid = self.countries[selected]
|
|
||||||
else:
|
|
||||||
woeid = self.cities[selected]
|
|
||||||
return woeid
|
|
||||||
|
|
||||||
def get_string(self):
|
|
||||||
return self.dialog.get_item()
|
|
@ -1,128 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
import webbrowser
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from wxUI.dialogs import update_profile, show_user
|
|
||||||
import logging
|
|
||||||
log = logging.getLogger("controller.user")
|
|
||||||
from tweepy.errors import TweepyException, Forbidden, NotFound
|
|
||||||
from sessions.twitter import utils
|
|
||||||
|
|
||||||
class profileController(object):
|
|
||||||
def __init__(self, session, user=None):
|
|
||||||
super(profileController, self).__init__()
|
|
||||||
self.file = None
|
|
||||||
self.session = session
|
|
||||||
self.user = user
|
|
||||||
if user == None:
|
|
||||||
self.get_data(screen_name=self.session.db["user_name"])
|
|
||||||
self.dialog = update_profile.updateProfileDialog()
|
|
||||||
self.fill_profile_fields()
|
|
||||||
self.uploaded = False
|
|
||||||
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self.get_data(screen_name=self.user)
|
|
||||||
except TweepyException as err:
|
|
||||||
if type(err) == NotFound:
|
|
||||||
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
if type(err) == Forbidden:
|
|
||||||
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
log.error("error %s" % (str(err)))
|
|
||||||
return
|
|
||||||
self.dialog = show_user.showUserProfile()
|
|
||||||
string = self.get_user_info()
|
|
||||||
self.dialog.set("text", string)
|
|
||||||
self.dialog.set_title(_(u"Information for %s") % (self.data.screen_name))
|
|
||||||
if self.data.url != None:
|
|
||||||
self.dialog.enable_url()
|
|
||||||
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
|
|
||||||
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
|
|
||||||
self.do_update()
|
|
||||||
|
|
||||||
def get_data(self, screen_name):
|
|
||||||
self.data = self.session.twitter.get_user(screen_name=screen_name)
|
|
||||||
if screen_name != self.session.db["user_name"]:
|
|
||||||
self.friendship_status = self.session.twitter.get_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
|
|
||||||
|
|
||||||
def fill_profile_fields(self):
|
|
||||||
self.dialog.set_name(self.data.name)
|
|
||||||
if self.data.url != None:
|
|
||||||
self.dialog.set_url(self.data.url)
|
|
||||||
if len(self.data.location) > 0:
|
|
||||||
self.dialog.set_location(self.data.location)
|
|
||||||
if len(self.data.description) > 0:
|
|
||||||
self.dialog.set_description(self.data.description)
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
file = self.dialog.upload_picture()
|
|
||||||
if file != None:
|
|
||||||
self.file = open(file, "rb")
|
|
||||||
self.uploaded = True
|
|
||||||
self.dialog.change_upload_button(self.uploaded)
|
|
||||||
|
|
||||||
def discard_image(self):
|
|
||||||
self.file = None
|
|
||||||
output.speak(_(u"Discarded"))
|
|
||||||
self.uploaded = False
|
|
||||||
self.dialog.change_upload_button(self.uploaded)
|
|
||||||
|
|
||||||
def upload_image(self, *args, **kwargs):
|
|
||||||
if self.uploaded == False:
|
|
||||||
self.get_image()
|
|
||||||
elif self.uploaded == True:
|
|
||||||
self.discard_image()
|
|
||||||
|
|
||||||
def do_update(self):
|
|
||||||
if self.user != None: return
|
|
||||||
name = self.dialog.get("name")
|
|
||||||
description = self.dialog.get("description")
|
|
||||||
location = self.dialog.get("location")
|
|
||||||
url = self.dialog.get("url")
|
|
||||||
if self.file != None:
|
|
||||||
try:
|
|
||||||
self.session.twitter.update_profile_image(image=self.file)
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak(u"Error %s" % (str(e)))
|
|
||||||
try:
|
|
||||||
self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak(u"Error %s." % (str(e)))
|
|
||||||
|
|
||||||
def get_user_info(self):
|
|
||||||
string = u""
|
|
||||||
string = string + _(u"Username: @%s\n") % (self.data.screen_name)
|
|
||||||
string = string + _(u"Name: %s\n") % (self.data.name)
|
|
||||||
if self.data.location != "":
|
|
||||||
string = string + _(u"Location: %s\n") % (self.data.location)
|
|
||||||
if self.data.url != None:
|
|
||||||
string = string+ _(u"URL: %s\n") % (self.data.entities["url"]["urls"][0]["expanded_url"])
|
|
||||||
if self.data.description != "":
|
|
||||||
if self.data.entities.get("description") != None and self.data.entities["description"].get("urls"):
|
|
||||||
self.data.description = utils.expand_urls(self.data.description, self.data.entities["description"])
|
|
||||||
string = string+ _(u"Bio: %s\n") % (self.data.description)
|
|
||||||
if self.data.protected == True: protected = _(u"Yes")
|
|
||||||
else: protected = _(u"No")
|
|
||||||
string = string+ _(u"Protected: %s\n") % (protected)
|
|
||||||
if hasattr(self, "friendship_status"):
|
|
||||||
relation = False
|
|
||||||
friendship = _(u"Relationship: ")
|
|
||||||
if self.friendship_status[0].following:
|
|
||||||
friendship += _(u"You follow {0}. ").format(self.data.name,)
|
|
||||||
relation = True
|
|
||||||
if self.friendship_status[1].following:
|
|
||||||
friendship += _(u"{0} is following you.").format(self.data.name,)
|
|
||||||
relation = True
|
|
||||||
if relation == True:
|
|
||||||
string = string+friendship+"\n"
|
|
||||||
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data.followers_count, self.data.friends_count)
|
|
||||||
if self.data.verified == True: verified = _(u"Yes")
|
|
||||||
else: verified = _(u"No")
|
|
||||||
string = string+ _(u"Verified: %s\n") % (verified)
|
|
||||||
string = string+ _(u"Tweets: %s\n") % (self.data.statuses_count)
|
|
||||||
string = string+ _(u"Likes: %s") % (self.data.favourites_count)
|
|
||||||
return string
|
|
||||||
|
|
||||||
def visit_url(self, *args, **kwargs):
|
|
||||||
webbrowser.open_new_tab(self.data.url)
|
|
@ -1,85 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from wxUI.dialogs import userActions
|
|
||||||
from pubsub import pub
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from extra.autocompletionUsers import completion
|
|
||||||
|
|
||||||
class userActionsController(object):
|
|
||||||
def __init__(self, buffer, users=[], default="follow"):
|
|
||||||
super(userActionsController, self).__init__()
|
|
||||||
self.buffer = buffer
|
|
||||||
self.session = buffer.session
|
|
||||||
self.dialog = userActions.UserActionsDialog(users, default)
|
|
||||||
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
|
||||||
if self.dialog.get_response() == widgetUtils.OK:
|
|
||||||
self.process_action()
|
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
|
||||||
c = completion.autocompletionUsers(self.dialog, self.session.session_id)
|
|
||||||
c.show_menu("dm")
|
|
||||||
|
|
||||||
def process_action(self):
|
|
||||||
action = self.dialog.get_action()
|
|
||||||
user = self.dialog.get_user()
|
|
||||||
if user == "": return
|
|
||||||
getattr(self, action)(user)
|
|
||||||
|
|
||||||
def follow(self, user):
|
|
||||||
try:
|
|
||||||
self.session.twitter.create_friendship(screen_name=user )
|
|
||||||
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def unfollow(self, user):
|
|
||||||
try:
|
|
||||||
id = self.session.twitter.destroy_friendship(screen_name=user )
|
|
||||||
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def mute(self, user):
|
|
||||||
try:
|
|
||||||
id = self.session.twitter.create_mute(screen_name=user )
|
|
||||||
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def unmute(self, user):
|
|
||||||
try:
|
|
||||||
id = self.session.twitter.destroy_mute(screen_name=user )
|
|
||||||
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def report(self, user):
|
|
||||||
try:
|
|
||||||
id = self.session.twitter.report_spam(screen_name=user )
|
|
||||||
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def block(self, user):
|
|
||||||
try:
|
|
||||||
id = self.session.twitter.create_block(screen_name=user )
|
|
||||||
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def unblock(self, user):
|
|
||||||
try:
|
|
||||||
id = self.session.twitter.destroy_block(screen_name=user )
|
|
||||||
except TweepyException as err:
|
|
||||||
output.speak("Error %s" % (str(err)), True)
|
|
||||||
|
|
||||||
def ignore_client(self, user):
|
|
||||||
tweet = self.buffer.get_right_tweet()
|
|
||||||
if hasattr(tweet, "sender"):
|
|
||||||
output.speak(_(u"You can't ignore direct messages"))
|
|
||||||
return
|
|
||||||
client = tweet.source
|
|
||||||
if client not in self.session.settings["twitter"]["ignored_clients"]:
|
|
||||||
self.session.settings["twitter"]["ignored_clients"].append(client)
|
|
||||||
self.session.settings.write()
|
|
@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Small utility dessigned to select users from the currently focused item or the autocomplete database. """
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
from wxUI.dialogs import utils
|
|
||||||
from extra.autocompletionUsers import completion
|
|
||||||
|
|
||||||
class userSelector(object):
|
|
||||||
|
|
||||||
def __init__(self, users, session_id, title=_("Select user")):
|
|
||||||
""" Creates a dialog that chooses an user selector, from where users who have the autocomplete database already filled can also use that feature.
|
|
||||||
|
|
||||||
:param users: lists of users extracted from the currently focused item.
|
|
||||||
:type users: list
|
|
||||||
:param session_id: ID of the session to instantiate autocomplete database.
|
|
||||||
:type session_id: str
|
|
||||||
:param title: Title of the user selector dialog.
|
|
||||||
:type title: str
|
|
||||||
"""
|
|
||||||
self.session_id = session_id
|
|
||||||
self.dlg = utils.selectUserDialog(users=users, title=title)
|
|
||||||
widgetUtils.connect_event(self.dlg.autocompletion, widgetUtils.BUTTON_PRESSED, self.on_autocomplete_users)
|
|
||||||
|
|
||||||
def on_autocomplete_users(self, *args, **kwargs):
|
|
||||||
""" performs user autocompletion, if configured properly. """
|
|
||||||
c = completion.autocompletionUsers(self.dlg, self.session_id)
|
|
||||||
c.show_menu("dm")
|
|
||||||
|
|
||||||
def get_user(self):
|
|
||||||
""" Actually shows the dialog and returns an user if the dialog was accepted, None otherwise.
|
|
||||||
|
|
||||||
:rtype: str or None
|
|
||||||
"""
|
|
||||||
if self.dlg.ShowModal() == wx.ID_OK:
|
|
||||||
user = self.dlg.get_user()
|
|
||||||
else:
|
|
||||||
user = None
|
|
||||||
self.dlg.Destroy()
|
|
||||||
return user
|
|
@ -1,182 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
############################################################
|
|
||||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
############################################################
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import str
|
|
||||||
from builtins import object
|
|
||||||
import widgetUtils
|
|
||||||
from . import wx_ui
|
|
||||||
from . import wx_transfer_dialogs
|
|
||||||
from . import transfer
|
|
||||||
import output
|
|
||||||
import tempfile
|
|
||||||
import sound
|
|
||||||
import os
|
|
||||||
import config
|
|
||||||
from pubsub import pub
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
import sound_lib
|
|
||||||
import logging
|
|
||||||
|
|
||||||
log = logging.getLogger("extra.AudioUploader.audioUploader")
|
|
||||||
|
|
||||||
class audioUploader(object):
|
|
||||||
def __init__(self, configFile, completed_callback):
|
|
||||||
self.config = configFile
|
|
||||||
super(audioUploader, self).__init__()
|
|
||||||
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
|
|
||||||
self.file = None
|
|
||||||
self.recorded = False
|
|
||||||
self.recording = None
|
|
||||||
self.playing = None
|
|
||||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
|
||||||
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
|
|
||||||
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
|
|
||||||
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
|
|
||||||
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
|
|
||||||
if self.dialog.get_response() == widgetUtils.OK:
|
|
||||||
self.postprocess()
|
|
||||||
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
|
|
||||||
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
|
|
||||||
output.speak(_(u"Attaching..."))
|
|
||||||
if self.dialog.get("services") == "SNDUp":
|
|
||||||
base_url = "https://sndup.net/post.php"
|
|
||||||
if len(self.config["sound"]["sndup_api_key"]) > 0:
|
|
||||||
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
|
|
||||||
else:
|
|
||||||
url = base_url
|
|
||||||
self.uploaderFunction = transfer.Upload(obj=self, field='file', url=url, filename=self.file, completed_callback=completed_callback)
|
|
||||||
pub.subscribe(self.uploaderDialog.update, "uploading")
|
|
||||||
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
|
|
||||||
|
|
||||||
def get_available_services(self):
|
|
||||||
services = []
|
|
||||||
services.append("SNDUp")
|
|
||||||
return services
|
|
||||||
|
|
||||||
def on_pause(self, *args, **kwargs):
|
|
||||||
if self.dialog.get("pause") == _(u"Pause"):
|
|
||||||
self.recording.pause()
|
|
||||||
self.dialog.set("pause", _(u"&Resume"))
|
|
||||||
elif self.dialog.get("pause") == _(u"Resume"):
|
|
||||||
self.recording.play()
|
|
||||||
self.dialog.set("pause", _(U"&Pause"))
|
|
||||||
|
|
||||||
def on_record(self, *args, **kwargs):
|
|
||||||
if self.recording != None:
|
|
||||||
self.stop_recording()
|
|
||||||
self.dialog.disable_control("pause")
|
|
||||||
else:
|
|
||||||
self.start_recording()
|
|
||||||
self.dialog.enable_control("pause")
|
|
||||||
|
|
||||||
def start_recording(self):
|
|
||||||
self.dialog.disable_control("attach_exists")
|
|
||||||
self.file = tempfile.mktemp(suffix='.wav')
|
|
||||||
self.recording = sound.recording(self.file)
|
|
||||||
self.recording.play()
|
|
||||||
self.dialog.set("record", _(u"&Stop"))
|
|
||||||
output.speak(_(u"Recording"))
|
|
||||||
|
|
||||||
def stop_recording(self):
|
|
||||||
self.recording.stop()
|
|
||||||
self.recording.free()
|
|
||||||
output.speak(_(u"Stopped"))
|
|
||||||
self.recorded = True
|
|
||||||
self.dialog.set("record", _(u"&Record"))
|
|
||||||
self.file_attached()
|
|
||||||
|
|
||||||
def file_attached(self):
|
|
||||||
self.dialog.set("pause", _(u"&Pause"))
|
|
||||||
self.dialog.disable_control("record")
|
|
||||||
self.dialog.enable_control("play")
|
|
||||||
self.dialog.enable_control("discard")
|
|
||||||
self.dialog.disable_control("attach_exists")
|
|
||||||
self.dialog.enable_control("attach")
|
|
||||||
self.dialog.play.SetFocus()
|
|
||||||
|
|
||||||
def on_discard(self, *args, **kwargs):
|
|
||||||
if self.playing:
|
|
||||||
self._stop()
|
|
||||||
if self.recording != None:
|
|
||||||
self.cleanup()
|
|
||||||
self.dialog.disable_control("attach")
|
|
||||||
self.dialog.disable_control("play")
|
|
||||||
self.file = None
|
|
||||||
self.dialog.enable_control("record")
|
|
||||||
self.dialog.enable_control("attach_exists")
|
|
||||||
self.dialog.record.SetFocus()
|
|
||||||
self.dialog.disable_control("discard")
|
|
||||||
self.recording = None
|
|
||||||
output.speak(_(u"Discarded"))
|
|
||||||
|
|
||||||
def on_play(self, *args, **kwargs):
|
|
||||||
if not self.playing:
|
|
||||||
call_threaded(self._play)
|
|
||||||
else:
|
|
||||||
self._stop()
|
|
||||||
|
|
||||||
def _play(self):
|
|
||||||
output.speak(_(u"Playing..."))
|
|
||||||
# try:
|
|
||||||
self.playing = sound_lib.stream.FileStream(file=str(self.file), flags=sound_lib.stream.BASS_UNICODE)
|
|
||||||
self.playing.play()
|
|
||||||
self.dialog.set("play", _(u"&Stop"))
|
|
||||||
try:
|
|
||||||
while self.playing.is_playing:
|
|
||||||
pass
|
|
||||||
self.dialog.set("play", _(u"&Play"))
|
|
||||||
self.playing.free()
|
|
||||||
self.playing = None
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _stop(self):
|
|
||||||
output.speak(_(u"Stopped"))
|
|
||||||
self.playing.stop()
|
|
||||||
self.playing.free()
|
|
||||||
self.dialog.set("play", _(u"&Play"))
|
|
||||||
self.playing = None
|
|
||||||
|
|
||||||
def postprocess(self):
|
|
||||||
if self.file.lower().endswith('.wav'):
|
|
||||||
output.speak(_(u"Recoding audio..."))
|
|
||||||
sound.recode_audio(self.file)
|
|
||||||
self.wav_file = self.file
|
|
||||||
self.file = '%s.ogg' % self.file[:-4]
|
|
||||||
|
|
||||||
def cleanup(self):
|
|
||||||
if self.playing and self.playing.is_playing:
|
|
||||||
self.playing.stop()
|
|
||||||
if self.recording != None:
|
|
||||||
if self.recording.is_playing:
|
|
||||||
self.recording.stop()
|
|
||||||
try:
|
|
||||||
self.recording.free()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
os.remove(self.file)
|
|
||||||
if hasattr(self, 'wav_file'):
|
|
||||||
os.remove(self.wav_file)
|
|
||||||
|
|
||||||
def on_attach_exists(self, *args, **kwargs):
|
|
||||||
self.file = self.dialog.get_file()
|
|
||||||
if self.file != False:
|
|
||||||
self.file_attached()
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
from past.utils import old_div
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
from .utils import convert_bytes
|
|
||||||
from pubsub import pub
|
|
||||||
log = logging.getLogger("extra.AudioUploader.transfer")
|
|
||||||
from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
|
|
||||||
import requests
|
|
||||||
import os
|
|
||||||
class Upload(object):
|
|
||||||
def __init__(self, field=None, obj=None, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
|
|
||||||
super(Upload, self).__init__(*args, **kwargs)
|
|
||||||
self.url=url
|
|
||||||
self.filename=filename
|
|
||||||
log.debug("Uploading audio to %s, filename %s" % (url, filename))
|
|
||||||
self.start_time = None
|
|
||||||
self.completed_callback = completed_callback
|
|
||||||
self.background_thread = None
|
|
||||||
self.transfer_rate = 0
|
|
||||||
self.local_filename=os.path.basename(self.filename)
|
|
||||||
if isinstance(self.local_filename, str):
|
|
||||||
self.local_filename=self.local_filename.encode(sys.getfilesystemencoding())
|
|
||||||
self.fin=open(self.filename, 'rb')
|
|
||||||
self.m = MultipartEncoder(fields={field:(self.local_filename, self.fin, "application/octet-stream")})
|
|
||||||
self.monitor = MultipartEncoderMonitor(self.m, self.progress_callback)
|
|
||||||
self.response=None
|
|
||||||
self.obj=obj
|
|
||||||
self.follow_location=follow_location
|
|
||||||
#the verbose parameter is deprecated and will be removed soon
|
|
||||||
|
|
||||||
def elapsed_time(self):
|
|
||||||
if not self.start_time:
|
|
||||||
return 0
|
|
||||||
return time.time() - self.start_time
|
|
||||||
|
|
||||||
def progress_callback(self, monitor):
|
|
||||||
progress = {}
|
|
||||||
progress["total"] = monitor.len
|
|
||||||
progress["current"] = monitor.bytes_read
|
|
||||||
if progress["current"] == 0:
|
|
||||||
progress["percent"] = 0
|
|
||||||
self.transfer_rate = 0
|
|
||||||
else:
|
|
||||||
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
|
|
||||||
self.transfer_rate = old_div(progress["current"], self.elapsed_time())
|
|
||||||
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
|
|
||||||
if self.transfer_rate:
|
|
||||||
progress["eta"] = old_div((progress["total"] - progress["current"]), self.transfer_rate)
|
|
||||||
else:
|
|
||||||
progress["eta"] = 0
|
|
||||||
pub.sendMessage("uploading", data=progress)
|
|
||||||
|
|
||||||
def perform_transfer(self):
|
|
||||||
log.debug("starting upload...")
|
|
||||||
self.start_time = time.time()
|
|
||||||
self.response=requests.post(url=self.url, data=self.monitor, headers={"Content-Type":self.m.content_type}, allow_redirects=self.follow_location, stream=True)
|
|
||||||
log.debug("Upload finished.")
|
|
||||||
self.complete_transfer()
|
|
||||||
|
|
||||||
def perform_threaded(self, *args, **kwargs):
|
|
||||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
|
||||||
self.background_thread.daemon = True
|
|
||||||
self.background_thread.start()
|
|
||||||
|
|
||||||
def complete_transfer(self):
|
|
||||||
if callable(self.completed_callback):
|
|
||||||
self.completed_callback(self.obj)
|
|
||||||
if hasattr(self,'fin') and callable(self.fin.close):
|
|
||||||
self.fin.close()
|
|
||||||
|
|
||||||
def get_url(self):
|
|
||||||
try:
|
|
||||||
data = self.response.json()
|
|
||||||
except:
|
|
||||||
return _("Error in file upload: {0}").format(self.data.content,)
|
|
||||||
if "url" in data and data["url"] != "0":
|
|
||||||
return data["url"]
|
|
||||||
elif "error" in data and data["error"] != "0":
|
|
||||||
return data["error"]
|
|
||||||
else:
|
|
||||||
return _("Error in file upload: {0}").format(self.data.content,)
|
|
@ -1,44 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import str
|
|
||||||
def convert_bytes(n):
|
|
||||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
|
||||||
if n >= P:
|
|
||||||
return '%.2fPb' % (float(n) / T)
|
|
||||||
elif n >= T:
|
|
||||||
return '%.2fTb' % (float(n) / T)
|
|
||||||
elif n >= G:
|
|
||||||
return '%.2fGb' % (float(n) / G)
|
|
||||||
elif n >= M:
|
|
||||||
return '%.2fMb' % (float(n) / M)
|
|
||||||
elif n >= K:
|
|
||||||
return '%.2fKb' % (float(n) / K)
|
|
||||||
else:
|
|
||||||
return '%d' % n
|
|
||||||
|
|
||||||
def seconds_to_string(seconds, precision=0):
|
|
||||||
day = seconds // 86400
|
|
||||||
hour = seconds // 3600
|
|
||||||
min = (seconds // 60) % 60
|
|
||||||
sec = seconds - (hour * 3600) - (min * 60)
|
|
||||||
sec_spec = "." + str(precision) + "f"
|
|
||||||
sec_string = sec.__format__(sec_spec)
|
|
||||||
string = ""
|
|
||||||
if day == 1:
|
|
||||||
string += _(u"%d day, ") % day
|
|
||||||
elif day >= 2:
|
|
||||||
string += _(u"%d days, ") % day
|
|
||||||
if (hour == 1):
|
|
||||||
string += _(u"%d hour, ") % hour
|
|
||||||
elif (hour >= 2):
|
|
||||||
string += _("%d hours, ") % hour
|
|
||||||
if (min == 1):
|
|
||||||
string += _(u"%d minute, ") % min
|
|
||||||
elif (min >= 2):
|
|
||||||
string += _(u"%d minutes, ") % min
|
|
||||||
if sec >= 0 and sec <= 2:
|
|
||||||
string += _(u"%s second") % sec_string
|
|
||||||
else:
|
|
||||||
string += _(u"%s seconds") % sec_string
|
|
||||||
return string
|
|
@ -1,63 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from .utils import *
|
|
||||||
import widgetUtils
|
|
||||||
|
|
||||||
class UploadDialog(widgetUtils.BaseDialog):
|
|
||||||
|
|
||||||
def __init__(self, filename, *args, **kwargs):
|
|
||||||
super(UploadDialog, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwargs)
|
|
||||||
self.pane = wx.Panel(self)
|
|
||||||
self.progress_bar = wx.Gauge(parent=self.pane)
|
|
||||||
fileBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
|
|
||||||
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
|
|
||||||
self.file.SetFocus()
|
|
||||||
fileBox.Add(fileLabel)
|
|
||||||
fileBox.Add(self.file)
|
|
||||||
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
|
|
||||||
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
currentAmountBox.Add(current_amount_label)
|
|
||||||
currentAmountBox.Add(self.current_amount)
|
|
||||||
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
|
|
||||||
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
totalSizeBox.Add(total_size_label)
|
|
||||||
totalSizeBox.Add(self.total_size)
|
|
||||||
speedBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
|
|
||||||
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
|
|
||||||
speedBox.Add(speedLabel)
|
|
||||||
speedBox.Add(self.speed)
|
|
||||||
etaBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
|
|
||||||
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
|
|
||||||
etaBox.Add(etaLabel)
|
|
||||||
etaBox.Add(self.eta)
|
|
||||||
self.create_buttons()
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
sizer.Add(fileBox)
|
|
||||||
sizer.Add(currentAmountBox)
|
|
||||||
sizer.Add(totalSizeBox)
|
|
||||||
sizer.Add(speedBox)
|
|
||||||
sizer.Add(etaBox)
|
|
||||||
sizer.Add(self.progress_bar)
|
|
||||||
self.pane.SetSizerAndFit(sizer)
|
|
||||||
|
|
||||||
def update(self, data):
|
|
||||||
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
|
|
||||||
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
|
|
||||||
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
|
|
||||||
wx.CallAfter(self.speed.SetValue, data["speed"])
|
|
||||||
if data["eta"]:
|
|
||||||
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
|
|
||||||
|
|
||||||
def create_buttons(self):
|
|
||||||
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
|
|
||||||
|
|
||||||
def get_response(self, fn):
|
|
||||||
wx.CallAfter(fn, 0.01)
|
|
||||||
self.ShowModal()
|
|
@ -1,79 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
############################################################
|
|
||||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
############################################################
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
import logging
|
|
||||||
log = logging.getLogger("extra.AudioUploader.wx_UI")
|
|
||||||
|
|
||||||
class audioDialog(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self, services):
|
|
||||||
log.debug("creating audio dialog.")
|
|
||||||
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
|
|
||||||
self.play = wx.Button(panel, -1, _(u"&Play"))
|
|
||||||
self.play.Disable()
|
|
||||||
self.pause = wx.Button(panel, -1, _(u"&Pause"))
|
|
||||||
self.pause.Disable()
|
|
||||||
self.record = wx.Button(panel, -1, _(u"&Record"))
|
|
||||||
self.record.SetFocus()
|
|
||||||
self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file"))
|
|
||||||
self.discard = wx.Button(panel, -1, _(u"&Discard"))
|
|
||||||
self.discard.Disable()
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Upload to"))
|
|
||||||
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
|
|
||||||
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
servicesBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
servicesBox.Add(self.services, 0, wx.ALL, 5)
|
|
||||||
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
|
|
||||||
self.attach.Disable()
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel"))
|
|
||||||
btnSizer.Add(self.play, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.pause, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.record, 0, wx.ALL, 5)
|
|
||||||
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
|
|
||||||
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
|
|
||||||
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
|
|
||||||
btnSizer2.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(servicesBox, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnSizer2, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def enable_control(self, control):
|
|
||||||
log.debug("Enabling control %s" % (control,))
|
|
||||||
if hasattr(self, control):
|
|
||||||
getattr(self, control).Enable()
|
|
||||||
|
|
||||||
def disable_control(self, control):
|
|
||||||
log.debug("Disabling control %s" % (control,))
|
|
||||||
if hasattr(self, control):
|
|
||||||
getattr(self, control).Disable()
|
|
||||||
|
|
||||||
def get_file(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return False
|
|
||||||
return openFileDialog.GetPath()
|
|
@ -1,2 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Autocompletion users for TWBlue. This package contains all needed code to support this feature, including automatic addition of users, management and code to show the autocompletion menu when an user is composing a tweet. """
|
|
@ -1,66 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Module to display the user autocompletion menu in tweet or direct message dialogs. """
|
|
||||||
import output
|
|
||||||
from . import storage
|
|
||||||
from . import wx_menu
|
|
||||||
|
|
||||||
class autocompletionUsers(object):
|
|
||||||
def __init__(self, window, session_id):
|
|
||||||
""" Class constructor. Displays a menu with users matching the specified pattern for autocompletion.
|
|
||||||
|
|
||||||
:param window: A wx control where the menu should be displayed. Normally this is going to be the wx.TextCtrl indicating the tweet's text or direct message recipient.
|
|
||||||
:type window: wx.Dialog
|
|
||||||
:param session_id: Session ID which calls this class. We will load the users database from this session.
|
|
||||||
:type session_id: str.
|
|
||||||
"""
|
|
||||||
super(autocompletionUsers, self).__init__()
|
|
||||||
self.window = window
|
|
||||||
self.db = storage.storage(session_id)
|
|
||||||
|
|
||||||
def show_menu(self, mode="tweet"):
|
|
||||||
""" displays a menu with possible users matching the specified pattern.
|
|
||||||
|
|
||||||
this menu can be displayed in tweet dialogs or in any other dialog where an username is expected. For tweet dialogs, the string should start with an at symbol (@), otherwise it won't match the pattern.
|
|
||||||
|
|
||||||
Of course, users must be already loaded in database before attempting this.
|
|
||||||
|
|
||||||
If no users are found, an error message will be spoken.
|
|
||||||
|
|
||||||
:param mode: this controls how the dialog will behave. Possible values are 'tweet' and 'dm'. In tweet mode, the matching pattern will be @user (@ is required), while in 'dm' mode the matching pattern will be anything written in the text control.
|
|
||||||
:type mode: str
|
|
||||||
"""
|
|
||||||
if mode == "tweet":
|
|
||||||
position = self.window.text.GetInsertionPoint()
|
|
||||||
text = self.window.text.GetValue()
|
|
||||||
text = text[:position]
|
|
||||||
try:
|
|
||||||
pattern = text.split()[-1]
|
|
||||||
except IndexError:
|
|
||||||
output.speak(_(u"You have to start writing"))
|
|
||||||
return
|
|
||||||
if pattern.startswith("@") == True:
|
|
||||||
menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode)
|
|
||||||
users = self.db.get_users(pattern[1:])
|
|
||||||
if len(users) > 0:
|
|
||||||
menu.append_options(users)
|
|
||||||
self.window.PopupMenu(menu, self.window.text.GetPosition())
|
|
||||||
menu.destroy()
|
|
||||||
else:
|
|
||||||
output.speak(_(u"There are no results in your users database"))
|
|
||||||
else:
|
|
||||||
output.speak(_(u"Autocompletion only works for users."))
|
|
||||||
elif mode == "dm":
|
|
||||||
text = self.window.cb.GetValue()
|
|
||||||
try:
|
|
||||||
pattern = text.split()[-1]
|
|
||||||
except IndexError:
|
|
||||||
output.speak(_(u"You have to start writing"))
|
|
||||||
return
|
|
||||||
menu = wx_menu.menu(self.window.cb, pattern, mode=mode)
|
|
||||||
users = self.db.get_users(pattern)
|
|
||||||
if len(users) > 0:
|
|
||||||
menu.append_options(users)
|
|
||||||
self.window.PopupMenu(menu, self.window.cb.GetPosition())
|
|
||||||
menu.destroy()
|
|
||||||
else:
|
|
||||||
output.speak(_(u"There are no results in your users database"))
|
|
@ -1,61 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Management of users in the local database for autocompletion. """
|
|
||||||
import time
|
|
||||||
import widgetUtils
|
|
||||||
from tweepy.cursor import Cursor
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
from . import storage, wx_manage
|
|
||||||
|
|
||||||
class autocompletionManage(object):
|
|
||||||
def __init__(self, session):
|
|
||||||
""" class constructor. Manages everything related to user autocompletion.
|
|
||||||
|
|
||||||
:param session: Sessiom where the autocompletion management has been requested.
|
|
||||||
:type session: sessions.base.Session.
|
|
||||||
"""
|
|
||||||
super(autocompletionManage, self).__init__()
|
|
||||||
self.session = session
|
|
||||||
# Instantiate database so we can perform modifications on it.
|
|
||||||
self.database = storage.storage(self.session.session_id)
|
|
||||||
|
|
||||||
def show_settings(self):
|
|
||||||
""" display user management dialog and connect events associated to it. """
|
|
||||||
self.dialog = wx_manage.autocompletionManageDialog()
|
|
||||||
self.users = self.database.get_all_users()
|
|
||||||
self.dialog.put_users(self.users)
|
|
||||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
|
||||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user)
|
|
||||||
self.dialog.get_response()
|
|
||||||
|
|
||||||
def update_list(self):
|
|
||||||
""" update users list in management dialog. This function is normallhy used after we modify the database in any way, so we can reload all users in the autocompletion user management list. """
|
|
||||||
item = self.dialog.users.get_selected()
|
|
||||||
self.dialog.users.clear()
|
|
||||||
self.users = self.database.get_all_users()
|
|
||||||
self.dialog.put_users(self.users)
|
|
||||||
self.dialog.users.select_item(item)
|
|
||||||
|
|
||||||
def add_user(self, *args, **kwargs):
|
|
||||||
""" Add a new Twitter username to the autocompletion database. """
|
|
||||||
usr = self.dialog.get_user()
|
|
||||||
if usr == False:
|
|
||||||
return
|
|
||||||
# check if user exists.
|
|
||||||
# ToDo: in case we want to adapt this for other networks we'd need to refactor this check.
|
|
||||||
try:
|
|
||||||
data = self.session.twitter.get_user(screen_name=usr)
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Exception raised when attempting to add an user to the autocomplete database manually.")
|
|
||||||
self.dialog.show_invalid_user_error()
|
|
||||||
return
|
|
||||||
self.database.set_user(data.screen_name, data.name, 0)
|
|
||||||
self.update_list()
|
|
||||||
|
|
||||||
def remove_user(self, *args, **kwargs):
|
|
||||||
""" Remove focused user from the autocompletion database. """
|
|
||||||
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
|
||||||
item = self.dialog.users.get_selected()
|
|
||||||
user = self.users[item]
|
|
||||||
self.database.remove_user(user[0])
|
|
||||||
self.update_list()
|
|
@ -1,110 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Scanning code for autocompletion feature on TWBlue. This module can retrieve user objects from the selected Twitter account automatically. """
|
|
||||||
import time
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from tweepy.cursor import Cursor
|
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from pubsub import pub
|
|
||||||
from . import wx_scan
|
|
||||||
from . import manage
|
|
||||||
from . import storage
|
|
||||||
|
|
||||||
class autocompletionScan(object):
|
|
||||||
def __init__(self, config, buffer, window):
|
|
||||||
""" Class constructor. This class will take care of scanning the selected Twitter account to populate the database with users automatically upon request.
|
|
||||||
|
|
||||||
:param config: Config for the session that will be scanned in search for users.
|
|
||||||
:type config: dict
|
|
||||||
:param buffer: home buffer for the focused session.
|
|
||||||
:type buffer: controller.buffers.twitter.base.baseBuffer
|
|
||||||
:param window: Main Window of TWBlue.
|
|
||||||
:type window:wx.Frame
|
|
||||||
"""
|
|
||||||
super(autocompletionScan, self).__init__()
|
|
||||||
self.config = config
|
|
||||||
self.buffer = buffer
|
|
||||||
self.window = window
|
|
||||||
|
|
||||||
def show_dialog(self):
|
|
||||||
self.dialog = wx_scan.autocompletionScanDialog()
|
|
||||||
self.dialog.set("friends", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
|
||||||
self.dialog.set("followers", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
|
||||||
if self.dialog.get_response() == widgetUtils.OK:
|
|
||||||
confirmation = wx_scan.confirm()
|
|
||||||
return confirmation
|
|
||||||
|
|
||||||
def prepare_progress_dialog(self):
|
|
||||||
self.progress_dialog = wx_scan.autocompletionScanProgressDialog()
|
|
||||||
# connect method to update progress dialog
|
|
||||||
pub.subscribe(self.on_update_progress, "on-update-progress")
|
|
||||||
self.progress_dialog.Show()
|
|
||||||
|
|
||||||
def on_update_progress(self, percent):
|
|
||||||
if percent > 100:
|
|
||||||
percent = 100
|
|
||||||
wx.CallAfter(self.progress_dialog.update, percent)
|
|
||||||
|
|
||||||
def scan(self):
|
|
||||||
""" Attempts to add all users selected by current user to the autocomplete database. """
|
|
||||||
ids = []
|
|
||||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends")
|
|
||||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers")
|
|
||||||
output.speak(_("Updating database... You can close this window now. A message will tell you when the process finishes."))
|
|
||||||
database = storage.storage(self.buffer.session.session_id)
|
|
||||||
percent = 0
|
|
||||||
# Retrieve ids of all following users
|
|
||||||
if self.dialog.get("friends") == True:
|
|
||||||
for i in Cursor(self.buffer.session.twitter.get_friend_ids, count=5000).items():
|
|
||||||
if str(i) not in ids:
|
|
||||||
ids.append(str(i))
|
|
||||||
# same step, but for followers.
|
|
||||||
if self.dialog.get("followers") == True:
|
|
||||||
try:
|
|
||||||
for i in Cursor(self.buffer.session.twitter.get_follower_ids, count=5000).items():
|
|
||||||
if str(i) not in ids:
|
|
||||||
ids.append(str(i))
|
|
||||||
except TweepyException:
|
|
||||||
wx.CallAfter(wx_scan.show_error)
|
|
||||||
return self.done()
|
|
||||||
# As next step requires batches of 100s users, let's split our user Ids so we won't break the param rules.
|
|
||||||
split_users = [ids[i:i + 100] for i in range(0, len(ids), 100)]
|
|
||||||
# store returned users in this list.
|
|
||||||
users = []
|
|
||||||
for z in split_users:
|
|
||||||
if len(z) == 0:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
results = self.buffer.session.twitter.lookup_users(user_id=z)
|
|
||||||
except TweepyException:
|
|
||||||
wx.CallAfter(wx_scan.show_error)
|
|
||||||
return self.done()
|
|
||||||
users.extend(results)
|
|
||||||
time.sleep(1)
|
|
||||||
percent = percent + (100/len(split_users))
|
|
||||||
pub.sendMessage("on-update-progress", percent=percent)
|
|
||||||
for user in users:
|
|
||||||
database.set_user(user.screen_name, user.name, 1)
|
|
||||||
wx.CallAfter(wx_scan.show_success, len(users))
|
|
||||||
self.done()
|
|
||||||
|
|
||||||
def done(self):
|
|
||||||
wx.CallAfter(self.progress_dialog.Destroy)
|
|
||||||
wx.CallAfter(self.dialog.Destroy)
|
|
||||||
pub.unsubscribe(self.on_update_progress, "on-update-progress")
|
|
||||||
|
|
||||||
def execute_at_startup(window, buffer, config):
|
|
||||||
database = storage.storage(buffer.session.session_id)
|
|
||||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
|
||||||
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
|
||||||
for i in buffer.session.db[buffer.name]:
|
|
||||||
database.set_user(i.screen_name, i.name, 1)
|
|
||||||
else:
|
|
||||||
database.remove_by_buffer(1)
|
|
||||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
|
||||||
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
|
||||||
for i in buffer.session.db[buffer.name]:
|
|
||||||
database.set_user(i.screen_name, i.name, 2)
|
|
||||||
else:
|
|
||||||
database.remove_by_buffer(2)
|
|
@ -1,52 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os, sqlite3, paths
|
|
||||||
|
|
||||||
class storage(object):
|
|
||||||
def __init__(self, session_id):
|
|
||||||
self.connection = sqlite3.connect(os.path.join(paths.config_path(), "%s/autocompletionUsers.dat" % (session_id)))
|
|
||||||
self.cursor = self.connection.cursor()
|
|
||||||
if self.table_exist("users") == False:
|
|
||||||
self.create_table()
|
|
||||||
|
|
||||||
def table_exist(self, table):
|
|
||||||
ask = self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % (table))
|
|
||||||
answer = ask.fetchone()
|
|
||||||
if answer == None:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_all_users(self):
|
|
||||||
self.cursor.execute("""select * from users""")
|
|
||||||
return self.cursor.fetchall()
|
|
||||||
|
|
||||||
def get_users(self, term):
|
|
||||||
self.cursor.execute("""SELECT * FROM users WHERE UPPER(user) LIKE :term OR UPPER(name) LIKE :term""", {"term": "%{}%".format(term.upper())})
|
|
||||||
return self.cursor.fetchall()
|
|
||||||
|
|
||||||
def set_user(self, screen_name, user_name, from_a_buffer):
|
|
||||||
self.cursor.execute("""insert or ignore into users values(?, ?, ?)""", (screen_name, user_name, from_a_buffer))
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
def remove_user(self, user):
|
|
||||||
self.cursor.execute("""DELETE FROM users WHERE user = ?""", (user,))
|
|
||||||
self.connection.commit()
|
|
||||||
return self.cursor.fetchone()
|
|
||||||
|
|
||||||
def remove_by_buffer(self, bufferType):
|
|
||||||
""" Removes all users saved on a buffer. BufferType is 0 for no buffer, 1 for friends and 2 for followers"""
|
|
||||||
self.cursor.execute("""DELETE FROM users WHERE from_a_buffer = ?""", (bufferType,))
|
|
||||||
self.connection.commit()
|
|
||||||
return self.cursor.fetchone()
|
|
||||||
|
|
||||||
def create_table(self):
|
|
||||||
self.cursor.execute("""
|
|
||||||
create table users(
|
|
||||||
user TEXT UNIQUE,
|
|
||||||
name TEXT,
|
|
||||||
from_a_buffer INTEGER
|
|
||||||
)""")
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.cursor.close()
|
|
||||||
self.connection.close()
|
|
@ -1,44 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
import application
|
|
||||||
|
|
||||||
class autocompletionManageDialog(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Editing {0} users database").format(application.name,))
|
|
||||||
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
|
|
||||||
sizer.Add(label, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.users.list, 0, wx.ALL, 5)
|
|
||||||
self.add = wx.Button(panel, -1, _(u"Add user"))
|
|
||||||
self.remove = wx.Button(panel, -1, _(u"Remove user"))
|
|
||||||
optionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
optionsBox.Add(self.add, 0, wx.ALL, 5)
|
|
||||||
optionsBox.Add(self.remove, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(optionsBox, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK)
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def put_users(self, users):
|
|
||||||
for i in users:
|
|
||||||
j = [i[0], i[1]]
|
|
||||||
self.users.insert_item(False, *j)
|
|
||||||
|
|
||||||
def get_user(self):
|
|
||||||
usr = False
|
|
||||||
userDlg = wx.TextEntryDialog(None, _(u"Twitter username"), _(u"Add user to database"))
|
|
||||||
if userDlg.ShowModal() == wx.ID_OK:
|
|
||||||
usr = userDlg.GetValue()
|
|
||||||
return usr
|
|
||||||
|
|
||||||
def show_invalid_user_error(self):
|
|
||||||
wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
|
@ -1,25 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class menu(wx.Menu):
|
|
||||||
def __init__(self, window, pattern, mode):
|
|
||||||
super(menu, self).__init__()
|
|
||||||
self.window = window
|
|
||||||
self.pattern = pattern
|
|
||||||
self.mode = mode
|
|
||||||
|
|
||||||
def append_options(self, options):
|
|
||||||
for i in options:
|
|
||||||
item = wx.MenuItem(self, wx.ID_ANY, "%s (@%s)" % (i[1], i[0]))
|
|
||||||
self.Append(item)
|
|
||||||
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
|
||||||
|
|
||||||
def select_text(self, ev, text):
|
|
||||||
if self.mode == "tweet":
|
|
||||||
self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" "))
|
|
||||||
elif self.mode == "dm":
|
|
||||||
self.window.SetValue(self.window.GetValue().replace(self.pattern, text))
|
|
||||||
self.window.SetInsertionPointEnd()
|
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
self.Destroy()
|
|
@ -1,48 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
import application
|
|
||||||
|
|
||||||
class autocompletionScanDialog(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(autocompletionScanDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.followers = wx.CheckBox(panel, -1, _("Add followers to database"))
|
|
||||||
self.friends = wx.CheckBox(panel, -1, _("Add friends to database"))
|
|
||||||
sizer.Add(self.followers, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.friends, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK)
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
class autocompletionScanProgressDialog(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(autocompletionScanProgressDialog, self).__init__(parent=None, id=wx.ID_ANY, title=_("Updating autocompletion database"), *args, **kwargs)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.progress_bar = wx.Gauge(parent=panel)
|
|
||||||
sizer.Add(self.progress_bar)
|
|
||||||
panel.SetSizerAndFit(sizer)
|
|
||||||
|
|
||||||
def update(self, percent):
|
|
||||||
self.progress_bar.SetValue(percent)
|
|
||||||
|
|
||||||
def confirm():
|
|
||||||
with wx.MessageDialog(None, _("This process will retrieve the users you selected from Twitter, and add them to the user autocomplete database. Please note that if there are many users or you have tried to perform this action less than 15 minutes ago, TWBlue may reach a limit in Twitter API calls when trying to load the users into the database. If this happens, we will show you an error, in which case you will have to try this process again in a few minutes. If this process ends with no error, you will be redirected back to the account settings dialog. Do you want to continue?"), _("Attention"), style=wx.ICON_QUESTION|wx.YES_NO) as result:
|
|
||||||
if result.ShowModal() == wx.ID_YES:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def show_success(users):
|
|
||||||
with wx.MessageDialog(None, _("TWBlue has imported {} users successfully.").format(users), _("Done")) as dlg:
|
|
||||||
dlg.ShowModal()
|
|
||||||
|
|
||||||
def show_error():
|
|
||||||
with wx.MessageDialog(None, _("Error adding users from Twitter. Please try again in about 15 minutes."), _("Error"), style=wx.ICON_ERROR) as dlg:
|
|
||||||
dlg.ShowModal()
|
|
@ -1,43 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
import os
|
|
||||||
import application
|
|
||||||
import platform
|
|
||||||
from ctypes import c_char_p
|
|
||||||
from libloader import load_library
|
|
||||||
import paths
|
|
||||||
#if application.snapshot == True:
|
|
||||||
# if platform.architecture()[0][:2] == "32":
|
|
||||||
# lib = load_library("snapshot_api_keys32", x86_path=paths.app_path("keys/lib"))
|
|
||||||
# else:
|
|
||||||
# lib = load_library("snapshot_api_keys64", x64_path=paths.app_path("keys/lib"))
|
|
||||||
#else:
|
|
||||||
if platform.architecture()[0][:2] == "32":
|
|
||||||
lib = load_library("stable_api_keys32", x86_path=os.path.join(paths.app_path(), "keys", "lib"))
|
|
||||||
else:
|
|
||||||
lib = load_library("stable_api_keys64", x64_path=os.path.join(paths.app_path(), "keys", "lib"))
|
|
||||||
|
|
||||||
# import linuxKeys
|
|
||||||
# lib = linuxKeys
|
|
||||||
|
|
||||||
keyring = None
|
|
||||||
|
|
||||||
def setup():
|
|
||||||
global keyring
|
|
||||||
if keyring == None:
|
|
||||||
keyring = Keyring()
|
|
||||||
|
|
||||||
class Keyring(object):
|
|
||||||
def __init__(self):
|
|
||||||
super(Keyring, self).__init__()
|
|
||||||
|
|
||||||
def _call_method(self, function):
|
|
||||||
result = getattr(lib, function)
|
|
||||||
result = c_char_p(result.__call__())
|
|
||||||
return result.value
|
|
||||||
|
|
||||||
def get(self, func):
|
|
||||||
if hasattr(application,func+"_override"):
|
|
||||||
return getattr(application,func+'_override')
|
|
||||||
return getattr(self, "_call_method")("get_"+func)
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,22 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
def get_api_key():
|
|
||||||
return "8pDLbyOW3saYnvSZ4uLFg\0"
|
|
||||||
|
|
||||||
def get_api_secret():
|
|
||||||
return "YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY\0";
|
|
||||||
|
|
||||||
#char *get_dropbox_api_key(){
|
|
||||||
#return "key\0";
|
|
||||||
#}
|
|
||||||
#char *get_dropbox_api_secret(){
|
|
||||||
#return "secret_key\0";
|
|
||||||
#}
|
|
||||||
#char *get_twishort_api_key(){
|
|
||||||
#return "key\0";
|
|
||||||
#}
|
|
||||||
#char *get_bts_user(){
|
|
||||||
#return "user\0";
|
|
||||||
#}
|
|
||||||
#char *get_bts_password(){
|
|
||||||
#return "pass\0";
|
|
||||||
#}
|
|
@ -21,7 +21,6 @@ import config
|
|||||||
import output
|
import output
|
||||||
import logging
|
import logging
|
||||||
import application
|
import application
|
||||||
import keys
|
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
import fixes
|
import fixes
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
@ -65,7 +64,6 @@ def setup():
|
|||||||
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
||||||
fixes.setup()
|
fixes.setup()
|
||||||
output.setup()
|
output.setup()
|
||||||
keys.setup()
|
|
||||||
from controller import settings
|
from controller import settings
|
||||||
from controller import mainController
|
from controller import mainController
|
||||||
from sessionmanager import sessionManager
|
from sessionmanager import sessionManager
|
||||||
|
@ -12,9 +12,7 @@ import config_utils
|
|||||||
import config
|
import config
|
||||||
import application
|
import application
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from tweepy.errors import TweepyException
|
|
||||||
from controller import settings
|
from controller import settings
|
||||||
from sessions.twitter import session as TwitterSession
|
|
||||||
from sessions.mastodon import session as MastodonSession
|
from sessions.mastodon import session as MastodonSession
|
||||||
from . import manager
|
from . import manager
|
||||||
from . import wxUI as view
|
from . import wxUI as view
|
||||||
@ -66,14 +64,7 @@ class sessionManagerController(object):
|
|||||||
output.speak("An exception was raised while attempting to clean malformed session data. See the error log for details. If this message persists, contact the developers.",True)
|
output.speak("An exception was raised while attempting to clean malformed session data. See the error log for details. If this message persists, contact the developers.",True)
|
||||||
os.exception("Exception thrown while removing malformed session")
|
os.exception("Exception thrown while removing malformed session")
|
||||||
continue
|
continue
|
||||||
if config_test.get("twitter") != None:
|
if config_test.get("mastodon") != None:
|
||||||
if application.twitter_support_enabled == False:
|
|
||||||
continue
|
|
||||||
name = _("{account_name} (Twitter)").format(account_name=config_test["twitter"]["user_name"])
|
|
||||||
if config_test["twitter"]["user_key"] != "" and config_test["twitter"]["user_secret"] != "":
|
|
||||||
sessionsList.append(name)
|
|
||||||
self.sessions.append(dict(type="twitter", id=i))
|
|
||||||
elif config_test.get("mastodon") != None:
|
|
||||||
name = _("{account_name}@{instance} (Mastodon)").format(account_name=config_test["mastodon"]["user_name"], instance=config_test["mastodon"]["instance"].replace("https://", ""))
|
name = _("{account_name}@{instance} (Mastodon)").format(account_name=config_test["mastodon"]["user_name"], instance=config_test["mastodon"]["instance"].replace("https://", ""))
|
||||||
if config_test["mastodon"]["instance"] != "" and config_test["mastodon"]["access_token"] != "":
|
if config_test["mastodon"]["instance"] != "" and config_test["mastodon"]["access_token"] != "":
|
||||||
sessionsList.append(name)
|
sessionsList.append(name)
|
||||||
@ -101,33 +92,27 @@ class sessionManagerController(object):
|
|||||||
if sessions.sessions.get(i.get("id")) != None:
|
if sessions.sessions.get(i.get("id")) != None:
|
||||||
continue
|
continue
|
||||||
# Create the session object based in session type.
|
# Create the session object based in session type.
|
||||||
if i.get("type") == "twitter":
|
if i.get("type") == "mastodon":
|
||||||
if application.twitter_support_enabled == False:
|
|
||||||
continue
|
|
||||||
s = TwitterSession.Session(i.get("id"))
|
|
||||||
elif i.get("type") == "mastodon":
|
|
||||||
s = MastodonSession.Session(i.get("id"))
|
s = MastodonSession.Session(i.get("id"))
|
||||||
s.get_configuration()
|
s.get_configuration()
|
||||||
if i.get("id") not in config.app["sessions"]["ignored_sessions"]:
|
if i.get("id") not in config.app["sessions"]["ignored_sessions"]:
|
||||||
try:
|
try:
|
||||||
s.login()
|
s.login()
|
||||||
except TweepyException:
|
except Exception as e:
|
||||||
self.show_auth_error(s.settings["twitter"]["user_name"])
|
log.exception("Exception during login on a TWBlue session.")
|
||||||
continue
|
continue
|
||||||
sessions.sessions[i.get("id")] = s
|
sessions.sessions[i.get("id")] = s
|
||||||
self.new_sessions[i.get("id")] = s
|
self.new_sessions[i.get("id")] = s
|
||||||
# self.view.destroy()
|
# self.view.destroy()
|
||||||
|
|
||||||
def show_auth_error(self, user_name):
|
def show_auth_error(self):
|
||||||
error = view.auth_error(user_name)
|
error = view.auth_error()
|
||||||
|
|
||||||
def manage_new_account(self, type):
|
def manage_new_account(self, type):
|
||||||
# Generic settings for all account types.
|
# Generic settings for all account types.
|
||||||
location = (str(time.time())[-6:])
|
location = (str(time.time())[-6:])
|
||||||
log.debug("Creating %s session in the %s path" % (type, location))
|
log.debug("Creating %s session in the %s path" % (type, location))
|
||||||
if type == "twitter":
|
if type == "mastodon":
|
||||||
s = TwitterSession.Session(location)
|
|
||||||
elif type == "mastodon":
|
|
||||||
s = MastodonSession.Session(location)
|
s = MastodonSession.Session(location)
|
||||||
result = s.authorise()
|
result = s.authorise()
|
||||||
if result == True:
|
if result == True:
|
||||||
|
@ -51,9 +51,6 @@ class sessionManagerWindow(wx.Dialog):
|
|||||||
|
|
||||||
def on_new_account(self, *args, **kwargs):
|
def on_new_account(self, *args, **kwargs):
|
||||||
menu = wx.Menu()
|
menu = wx.Menu()
|
||||||
if application.twitter_support_enabled:
|
|
||||||
twitter = menu.Append(wx.ID_ANY, _("Twitter"))
|
|
||||||
menu.Bind(wx.EVT_MENU, self.on_new_twitter_account, twitter)
|
|
||||||
mastodon = menu.Append(wx.ID_ANY, _("Mastodon"))
|
mastodon = menu.Append(wx.ID_ANY, _("Mastodon"))
|
||||||
menu.Bind(wx.EVT_MENU, self.on_new_mastodon_account, mastodon)
|
menu.Bind(wx.EVT_MENU, self.on_new_mastodon_account, mastodon)
|
||||||
self.PopupMenu(menu, self.new.GetPosition())
|
self.PopupMenu(menu, self.new.GetPosition())
|
||||||
@ -65,15 +62,6 @@ class sessionManagerWindow(wx.Dialog):
|
|||||||
if response == wx.ID_YES:
|
if response == wx.ID_YES:
|
||||||
pub.sendMessage("sessionmanager.new_account", type="mastodon")
|
pub.sendMessage("sessionmanager.new_account", type="mastodon")
|
||||||
|
|
||||||
def on_new_twitter_account(self, *args, **kwargs):
|
|
||||||
if application.twitter_support_enabled == False:
|
|
||||||
return
|
|
||||||
dlg = wx.MessageDialog(self, _(u"The request to authorize your Twitter account will be opened in your browser. You only need to do this once. Would you like to continue?"), _(u"Authorization"), wx.YES_NO)
|
|
||||||
response = dlg.ShowModal()
|
|
||||||
dlg.Destroy()
|
|
||||||
if response == wx.ID_YES:
|
|
||||||
pub.sendMessage("sessionmanager.new_account", type="twitter")
|
|
||||||
|
|
||||||
def add_new_session_to_list(self):
|
def add_new_session_to_list(self):
|
||||||
total = self.list.get_count()
|
total = self.list.get_count()
|
||||||
name = _(u"Authorized account %d") % (total+1)
|
name = _(u"Authorized account %d") % (total+1)
|
||||||
@ -104,12 +92,8 @@ class sessionManagerWindow(wx.Dialog):
|
|||||||
def remove_session(self, sessionID):
|
def remove_session(self, sessionID):
|
||||||
self.list.remove_item(sessionID)
|
self.list.remove_item(sessionID)
|
||||||
|
|
||||||
|
|
||||||
def hide_configuration(self):
|
def hide_configuration(self):
|
||||||
self.configuration.Hide()
|
self.configuration.Hide()
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self.Destroy()
|
self.Destroy()
|
||||||
|
|
||||||
def auth_error(user_name):
|
|
||||||
return wx.MessageDialog(None, _("TWBlue is unable to authenticate the account for {} in Twitter. It might be due to an invalid or expired token, revoqued access to the application, or after an account reactivation. Please remove the account manually from your Twitter sessions in order to stop seeing this message.").format(user_name,), _("Authentication error for session {}").format(user_name,), wx.OK).ShowModal()
|
|
||||||
|
@ -195,8 +195,6 @@ class Session(base.baseSession):
|
|||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
if tries == 4 and finished == False:
|
if tries == 4 and finished == False:
|
||||||
raise e
|
raise e
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
if report_success:
|
if report_success:
|
||||||
output.speak(_("%s succeeded.") % action)
|
output.speak(_("%s succeeded.") % action)
|
||||||
if _sound != None:
|
if _sound != None:
|
||||||
|
@ -1 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
@ -1,143 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from . import utils
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import output
|
|
||||||
import languageHandler
|
|
||||||
import arrow
|
|
||||||
import logging
|
|
||||||
import config
|
|
||||||
from .long_tweets import twishort, tweets
|
|
||||||
from .utils import StripChars
|
|
||||||
log = logging.getLogger("compose")
|
|
||||||
|
|
||||||
chars = "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
|
|
||||||
def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None):
|
|
||||||
""" It receives a tweet and returns a list with the user, text for the tweet or message, date and the client where user is."""
|
|
||||||
original_date = arrow.get(tweet.created_at, locale="en")
|
|
||||||
if relative_times == True:
|
|
||||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts = original_date.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
|
||||||
if hasattr(tweet, "message"):
|
|
||||||
value = "message"
|
|
||||||
elif hasattr(tweet, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
if hasattr(tweet, "retweeted_status") and value != "message":
|
|
||||||
text = utils.clean_mentions(StripChars(getattr(tweet.retweeted_status, value)))
|
|
||||||
else:
|
|
||||||
text = utils.clean_mentions(StripChars(getattr(tweet, value)))
|
|
||||||
if show_screen_names:
|
|
||||||
user = session.get_user(tweet.user).screen_name
|
|
||||||
else:
|
|
||||||
user = session.get_user(tweet.user).name
|
|
||||||
source = re.sub(r"(?s)<.*?>", "", tweet.source)
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
if hasattr(tweet, "message") == False and hasattr(tweet.retweeted_status, "is_quote_status") == False:
|
|
||||||
text = "RT @%s: %s" % (session.get_user(tweet.retweeted_status.user).screen_name, text)
|
|
||||||
elif hasattr(tweet.retweeted_status, "is_quote_status"):
|
|
||||||
text = "%s" % (text)
|
|
||||||
else:
|
|
||||||
text = "RT @%s: %s" % (session.get_user(tweet.retweeted_status.user).screen_name, text)
|
|
||||||
if not hasattr(tweet, "message"):
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
if hasattr(tweet.retweeted_status, "entities"):
|
|
||||||
text = utils.expand_urls(text, tweet.retweeted_status.entities)
|
|
||||||
else:
|
|
||||||
if hasattr(tweet, "entities"):
|
|
||||||
text = utils.expand_urls(text, tweet.entities)
|
|
||||||
if config.app['app-settings']['handle_longtweets']: pass
|
|
||||||
return [user+", ", text, ts+", ", source]
|
|
||||||
|
|
||||||
def compose_direct_message(item, db, relative_times, show_screen_names=False, session=None):
|
|
||||||
# Let's remove the last 3 digits in the timestamp string.
|
|
||||||
# Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it.
|
|
||||||
original_date = arrow.get(int(item.created_timestamp))
|
|
||||||
if relative_times == True:
|
|
||||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts = original_date.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
|
||||||
text = StripChars(item.message_create["message_data"]["text"])
|
|
||||||
source = "DM"
|
|
||||||
sender = session.get_user(item.message_create["sender_id"])
|
|
||||||
if db["user_name"] == sender.screen_name:
|
|
||||||
if show_screen_names:
|
|
||||||
user = _(u"Dm to %s ") % (session.get_user(item.message_create["target"]["recipient_id"]).screen_name)
|
|
||||||
else:
|
|
||||||
user = _(u"Dm to %s ") % (session.get_user(item.message_create["target"]["recipient_id"]).name)
|
|
||||||
else:
|
|
||||||
if show_screen_names:
|
|
||||||
user = sender.screen_name
|
|
||||||
else:
|
|
||||||
user = sender.name
|
|
||||||
if text[-1] in chars: text=text+"."
|
|
||||||
text = utils.expand_urls(text, item.message_create["message_data"]["entities"])
|
|
||||||
return [user+", ", text, ts+", ", source]
|
|
||||||
|
|
||||||
def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False, session=None):
|
|
||||||
""" It receives a tweet and returns a list with the user, text for the tweet or message, date and the client where user is."""
|
|
||||||
if hasattr(quoted_tweet, "retweeted_status"):
|
|
||||||
if hasattr(quoted_tweet.retweeted_status, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
text = StripChars(getattr(quoted_tweet.retweeted_status, value))
|
|
||||||
else:
|
|
||||||
if hasattr(quoted_tweet, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
text = utils.clean_mentions(StripChars(getattr(quoted_tweet, value)))
|
|
||||||
if show_screen_names:
|
|
||||||
quoting_user = session.get_user(quoted_tweet.user).screen_name
|
|
||||||
else:
|
|
||||||
quoting_user = session.get_user(quoted_tweet.user).name
|
|
||||||
source = quoted_tweet.source
|
|
||||||
if hasattr(quoted_tweet, "retweeted_status"):
|
|
||||||
text = "rt @%s: %s" % (session.get_user(quoted_tweet.retweeted_status.user).screen_name, text)
|
|
||||||
if text[-1] in chars: text=text+"."
|
|
||||||
original_user = session.get_user(original_tweet.user).screen_name
|
|
||||||
if hasattr(original_tweet, "message"):
|
|
||||||
original_text = original_tweet.message
|
|
||||||
elif hasattr(original_tweet, "full_text"):
|
|
||||||
original_text = utils.clean_mentions(StripChars(original_tweet.full_text))
|
|
||||||
else:
|
|
||||||
original_text = utils.clean_mentions(StripChars(original_tweet.text))
|
|
||||||
quoted_tweet.message = _(u"{0}. Quoted tweet from @{1}: {2}").format( text, original_user, original_text)
|
|
||||||
quoted_tweet = tweets.clear_url(quoted_tweet)
|
|
||||||
if hasattr(original_tweet, "entities") and original_tweet.entities.get("urls"):
|
|
||||||
if hasattr(quoted_tweet, "entities") == False:
|
|
||||||
quoted_tweet.entities = {}
|
|
||||||
if quoted_tweet.entities.get("urls") == None:
|
|
||||||
quoted_tweet.entities["urls"] = []
|
|
||||||
quoted_tweet.entities["urls"].extend(original_tweet.entities["urls"])
|
|
||||||
return quoted_tweet
|
|
||||||
|
|
||||||
def compose_followers_list(tweet, db, relative_times=True, show_screen_names=False, session=None):
|
|
||||||
original_date = arrow.get(tweet.created_at, locale="en")
|
|
||||||
if relative_times == True:
|
|
||||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts = original_date.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
|
||||||
if hasattr(tweet, "status"):
|
|
||||||
original_date2 = arrow.get(tweet.status.created_at, locale="en")
|
|
||||||
if relative_times:
|
|
||||||
ts2 = original_date2.humanize(locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts2 = original_date2.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts2 = _("Unavailable")
|
|
||||||
return [_(u"%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined Twitter %s") % (tweet.name, tweet.screen_name, tweet.followers_count, tweet.friends_count, tweet.statuses_count, ts2, ts)]
|
|
||||||
|
|
||||||
def compose_list(list):
|
|
||||||
name = list.name
|
|
||||||
if list.description == None: description = _(u"No description available")
|
|
||||||
else: description = list.description
|
|
||||||
user = list.user.name
|
|
||||||
members = str(list.member_count)
|
|
||||||
if list.mode == "private": status = _(u"private")
|
|
||||||
else: status = _(u"public")
|
|
||||||
return [name, description, user, members, status]
|
|
@ -1,2 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" this package holds different modules to extract information regarding long tweets. A long tweet contains more than one tweet (such a quoted tweet), or is made via services like twishort."""
|
|
@ -1,52 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
############################################################
|
|
||||||
# Copyright (c) 2015 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
############################################################
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from sessions.twitter import utils
|
|
||||||
|
|
||||||
def is_long(tweet):
|
|
||||||
""" Check if the passed tweet contains a quote in its metadata.
|
|
||||||
tweet dict: a tweet dictionary.
|
|
||||||
returns True if a quote is detected, False otherwise."""
|
|
||||||
if hasattr(tweet, "quoted_status_id") and hasattr(tweet, "quoted_status"):
|
|
||||||
return tweet.quoted_status_id
|
|
||||||
elif hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "quoted_status_id") and hasattr(tweet.retweeted_status, "quoted_status"):
|
|
||||||
return tweet.retweeted_status.quoted_status_id
|
|
||||||
return False
|
|
||||||
|
|
||||||
def clear_url(tweet):
|
|
||||||
""" Reads data from a quoted tweet and removes the link to the Status from the tweet's text.
|
|
||||||
tweet dict: a tweet dictionary.
|
|
||||||
returns a tweet dictionary without the URL to the status ID in its text to display."""
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
if hasattr(tweet.retweeted_status, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
urls = utils.find_urls_in_text(getattr(tweet.retweeted_status, value))
|
|
||||||
try: tweet.message = tweet.message.replace(urls[-1], "")
|
|
||||||
except IndexError: pass
|
|
||||||
else:
|
|
||||||
if hasattr(tweet, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
urls = utils.find_urls_in_text(getattr(tweet, value))
|
|
||||||
try: tweet.message = tweet.message.replace(urls[-1], "")
|
|
||||||
except IndexError: pass
|
|
||||||
return tweet
|
|
@ -1,98 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
############################################################
|
|
||||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
############################################################
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import range
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import keys
|
|
||||||
from requests_oauthlib import OAuth1Session
|
|
||||||
from sessions.twitter import utils
|
|
||||||
|
|
||||||
log = logging.getLogger("long_tweets.twishort")
|
|
||||||
|
|
||||||
def get_twishort_uri(url):
|
|
||||||
""" Takes A twishort URl and returns the twishort ID.
|
|
||||||
url str: an url like http://twishort.com/id.
|
|
||||||
returns a twishort ID if the URL is valid, False otherwise."""
|
|
||||||
try:
|
|
||||||
return url.split("twishort.com/")[1]
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_long(tweet):
|
|
||||||
""" Check if the passed tweet is made with Twishort.
|
|
||||||
returns True if is a long tweet, False otherwise."""
|
|
||||||
long = False
|
|
||||||
if hasattr(tweet, "entities") and tweet.entities.get("urls"):
|
|
||||||
for url in range(0, len(tweet.entities["urls"])):
|
|
||||||
try:
|
|
||||||
if tweet.entities["urls"][url] != None and "twishort.com" in tweet.entities["urls"][url]["expanded_url"]:
|
|
||||||
long = get_twishort_uri(tweet.entities["urls"][url]["expanded_url"])
|
|
||||||
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):
|
|
||||||
""" Get Twishort's full text.
|
|
||||||
uri str: Twishort's identifier.
|
|
||||||
returns the contents of the tweet."""
|
|
||||||
try:
|
|
||||||
r = requests.get("http://api.twishort.com/1.1/get.json", params={"uri": uri, "api_key": keys.keyring.get("twishort_api_key")})
|
|
||||||
msg = r.json()["text"]
|
|
||||||
# Try to parse possible HTML entities.
|
|
||||||
from sessions.twitter.compose import StripChars
|
|
||||||
msg = StripChars(msg)
|
|
||||||
return msg
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def create_tweet(user_token, user_secret, text, media=0):
|
|
||||||
""" Send a tweet to be extended by using Twishort.
|
|
||||||
user_token, user_secret str: Twitter user access key and secret, used by TWBlue to authorise against Twitter.
|
|
||||||
text str: Tweet text, max 10000 characters.
|
|
||||||
media int: Not used currently.
|
|
||||||
Returns text to be placed in the Tweet if the post has been succeeded, 0 otherwise."""
|
|
||||||
twitter = OAuth1Session(keys.keyring.get("api_key"), client_secret=keys.keyring.get("api_secret"), resource_owner_key=user_token, resource_owner_secret=user_secret)
|
|
||||||
twishort_key=keys.keyring.get("twishort_api_key")
|
|
||||||
x_auth_service_provider = "https://api.twitter.com/1.1/account/verify_credentials.json"
|
|
||||||
twishort_post_url = "http://api.twishort.com/1.1/post.json"
|
|
||||||
twishort_update_ids_url = "http://api.twishort.com/1.1/update_ids.json"
|
|
||||||
r=requests.Request('GET', x_auth_service_provider)
|
|
||||||
prep=twitter.prepare_request(r)
|
|
||||||
resp=twitter.send(prep)
|
|
||||||
twitter.headers={
|
|
||||||
'X-Auth-Service-Provider':x_auth_service_provider,
|
|
||||||
'X-Verify-Credentials-Authorization':prep.headers['Authorization'],
|
|
||||||
}
|
|
||||||
data = {'api_key':twishort_key,
|
|
||||||
"text": text.encode("utf-8"),
|
|
||||||
"media": media}
|
|
||||||
response = twitter.post(twishort_post_url, data=data)
|
|
||||||
try:
|
|
||||||
return response.json()["text_to_tweet"]
|
|
||||||
except:
|
|
||||||
print("There was a problem creating a long tweet")
|
|
||||||
return 0
|
|
@ -1,37 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Strips unneeded tweet information in order to store tweet objects by using less memory. This is especially useful when buffers start to contain more than a certain amount of items. """
|
|
||||||
from tweepy.models import Status
|
|
||||||
|
|
||||||
def reduce_tweet(tweet):
|
|
||||||
""" generates a new Tweet model with the fields we currently need, excluding everything else including null values and empty collections. """
|
|
||||||
allowed_values = ["created_at", "id", "full_text", "text", "message", "in_reply_to_status_id", "in_reply_to_user_id", "is_quote_status", "lang", "source", "coordinates", "quoted_status_id", "extended_entities"]
|
|
||||||
allowed_entities = ["hashtags", "media", "urls", "user_mentions", "polls"]
|
|
||||||
status_dict = {}
|
|
||||||
for key in allowed_values:
|
|
||||||
if tweet._json.get(key):
|
|
||||||
status_dict[key] = tweet._json[key]
|
|
||||||
entities = dict()
|
|
||||||
for key in allowed_entities:
|
|
||||||
if tweet._json["entities"].get(key) and tweet._json["entities"].get(key) != None:
|
|
||||||
entities[key] = tweet._json["entities"][key]
|
|
||||||
status_dict["entities"] = entities
|
|
||||||
# If tweet comes from the cached database, it does not include an API, so we can pass None here as we do not use that reference to tweepy's API.
|
|
||||||
if hasattr(tweet, "_api"):
|
|
||||||
api = tweet._api
|
|
||||||
else:
|
|
||||||
api = None
|
|
||||||
status = Status().parse(api=api, json=status_dict)
|
|
||||||
# Quotes and retweets are different objects. So we parse a new tweet when we have a quoted or retweeted status here.
|
|
||||||
if tweet._json.get("quoted_status"):
|
|
||||||
quoted_tweet = reduce_tweet(tweet.quoted_status)
|
|
||||||
status.quoted_status = quoted_tweet
|
|
||||||
if tweet._json.get("retweeted_status"):
|
|
||||||
retweeted_tweet = reduce_tweet(tweet.retweeted_status)
|
|
||||||
status.retweeted_status = retweeted_tweet
|
|
||||||
# Adds user ID to here so we can reference it later.
|
|
||||||
# Sometimes, the conversations buffer would send an already reduced tweet here so we will need to return it as is.
|
|
||||||
if isinstance(tweet.user, str) == False:
|
|
||||||
status.user = tweet.user.id_str
|
|
||||||
else:
|
|
||||||
return tweet
|
|
||||||
return status
|
|
@ -1,671 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" This is the main session needed to access all Twitter Features."""
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
import webbrowser
|
|
||||||
import wx
|
|
||||||
import tweepy
|
|
||||||
import demoji
|
|
||||||
import config
|
|
||||||
import output
|
|
||||||
import application
|
|
||||||
import appkeys
|
|
||||||
from pubsub import pub
|
|
||||||
from tweepy.errors import TweepyException, Forbidden, NotFound
|
|
||||||
from tweepy.models import User as UserModel
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
from keys import keyring
|
|
||||||
from sessions import base
|
|
||||||
from sessions.twitter import utils, compose
|
|
||||||
from sessions.twitter.long_tweets import tweets, twishort
|
|
||||||
from . import reduce, streaming
|
|
||||||
from .wxUI import authorisationDialog
|
|
||||||
|
|
||||||
log = logging.getLogger("sessions.twitterSession")
|
|
||||||
|
|
||||||
class Session(base.baseSession):
|
|
||||||
""" A session object where we will save configuration, the twitter object and a local storage for saving the items retrieved through the Twitter API methods"""
|
|
||||||
|
|
||||||
def order_buffer(self, name, data, ignore_older=True):
|
|
||||||
""" Put new items in the local database.
|
|
||||||
name str: The name for the buffer stored in the dictionary.
|
|
||||||
data list: A list with tweets.
|
|
||||||
ignore_older bool: if set to True, items older than the first element on the list will be ignored.
|
|
||||||
returns the number of items that have been added in this execution"""
|
|
||||||
if name == "direct_messages":
|
|
||||||
return self.order_direct_messages(data)
|
|
||||||
num = 0
|
|
||||||
last_id = None
|
|
||||||
if self.db.get(name) == None:
|
|
||||||
self.db[name] = []
|
|
||||||
if self.db.get("users") == None:
|
|
||||||
self.db["users"] = {}
|
|
||||||
objects = self.db[name]
|
|
||||||
if ignore_older and len(self.db[name]) > 0:
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False:
|
|
||||||
last_id = self.db[name][0].id
|
|
||||||
else:
|
|
||||||
last_id = self.db[name][-1].id
|
|
||||||
self.add_users_from_results(data)
|
|
||||||
for i in data:
|
|
||||||
if ignore_older and last_id != None:
|
|
||||||
if i.id < last_id:
|
|
||||||
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id))
|
|
||||||
continue
|
|
||||||
if utils.find_item(i, self.db[name]) == None and utils.is_allowed(i, self.settings, name) == True:
|
|
||||||
if i == False: continue
|
|
||||||
reduced_object = reduce.reduce_tweet(i)
|
|
||||||
reduced_object = self.check_quoted_status(reduced_object)
|
|
||||||
reduced_object = self.check_long_tweet(reduced_object)
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False: objects.append(reduced_object)
|
|
||||||
else: objects.insert(0, reduced_object)
|
|
||||||
num = num+1
|
|
||||||
self.db[name] = objects
|
|
||||||
return num
|
|
||||||
|
|
||||||
def order_people(self, name, data):
|
|
||||||
""" Put new items on the local database. Useful for cursored buffers (followers, friends, users of a list and searches)
|
|
||||||
name str: The name for the buffer stored in the dictionary.
|
|
||||||
data list: A list with items and some information about cursors.
|
|
||||||
returns the number of items that have been added in this execution"""
|
|
||||||
num = 0
|
|
||||||
if (name in self.db) == False:
|
|
||||||
self.db[name] = []
|
|
||||||
objects = self.db[name]
|
|
||||||
for i in data:
|
|
||||||
if utils.find_item(i, self.db[name]) == None:
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
|
|
||||||
else: objects.insert(0, i)
|
|
||||||
num = num+1
|
|
||||||
self.db[name] = objects
|
|
||||||
return num
|
|
||||||
|
|
||||||
def order_direct_messages(self, data):
|
|
||||||
""" Add incoming and sent direct messages to their corresponding database items.
|
|
||||||
data list: A list of direct messages to add.
|
|
||||||
returns the number of incoming messages processed in this execution, and sends an event with data regarding amount of sent direct messages added."""
|
|
||||||
incoming = 0
|
|
||||||
sent = 0
|
|
||||||
if ("direct_messages" in self.db) == False:
|
|
||||||
self.db["direct_messages"] = []
|
|
||||||
if ("sent_direct_messages" in self.db) == False:
|
|
||||||
self.db["sent_direct_messages"] = []
|
|
||||||
objects = self.db["direct_messages"]
|
|
||||||
sent_objects = self.db["sent_direct_messages"]
|
|
||||||
for i in data:
|
|
||||||
if i.message_create["sender_id"] == self.db["user_id"]:
|
|
||||||
if "sent_direct_messages" in self.db and utils.find_item(i, self.db["sent_direct_messages"]) == None:
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False: sent_objects.append(i)
|
|
||||||
else: sent_objects.insert(0, i)
|
|
||||||
sent = sent+1
|
|
||||||
else:
|
|
||||||
if utils.find_item(i, self.db["direct_messages"]) == None:
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
|
|
||||||
else: objects.insert(0, i)
|
|
||||||
incoming = incoming+1
|
|
||||||
self.db["direct_messages"] = objects
|
|
||||||
self.db["sent_direct_messages"] = sent_objects
|
|
||||||
pub.sendMessage("twitter.sent_dms_updated", total=sent, session_name=self.get_name())
|
|
||||||
return incoming
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Session, self).__init__(*args, **kwargs)
|
|
||||||
# Adds here the optional cursors objects.
|
|
||||||
cursors = dict(direct_messages=-1)
|
|
||||||
self.db["cursors"] = cursors
|
|
||||||
self.reconnection_function_active = False
|
|
||||||
self.counter = 0
|
|
||||||
self.lists = []
|
|
||||||
# As users are cached for accessing them with not too many twitter calls,
|
|
||||||
# there could be a weird situation where a deleted user who sent direct messages to the current account will not be able to be retrieved at twitter.
|
|
||||||
# So we need to store an "user deleted" object in the cache, but have the ID of the deleted user in a local reference.
|
|
||||||
# This will be especially useful because if the user reactivates their account later, TWblue will try to retrieve such user again at startup.
|
|
||||||
# If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object.
|
|
||||||
self.deleted_users = {}
|
|
||||||
self.type = "twitter"
|
|
||||||
pub.subscribe(self.handle_new_status, "twitter.new_status")
|
|
||||||
pub.subscribe(self.handle_connected, "twitter.stream_connected")
|
|
||||||
|
|
||||||
# @_require_configuration
|
|
||||||
def login(self, verify_credentials=True):
|
|
||||||
""" Log into twitter using credentials from settings.
|
|
||||||
if the user account isn't authorised, it needs to call self.authorise() before login."""
|
|
||||||
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
|
|
||||||
try:
|
|
||||||
log.debug("Logging in to twitter...")
|
|
||||||
self.auth = tweepy.OAuth1UserHandler(consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"])
|
|
||||||
self.twitter = tweepy.API(self.auth)
|
|
||||||
self.twitter_v2 = tweepy.Client(consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"])
|
|
||||||
if verify_credentials == True:
|
|
||||||
self.credentials = self.twitter.verify_credentials()
|
|
||||||
self.settings["twitter"]["user_name"] = self.credentials.screen_name
|
|
||||||
self.db["user_name"] = self.credentials.screen_name
|
|
||||||
self.db["user_id"] = self.credentials.id_str
|
|
||||||
self.logged = True
|
|
||||||
log.debug("Logged.")
|
|
||||||
self.counter = 0
|
|
||||||
except Exception as e:
|
|
||||||
log.exception("The login attempt failed.")
|
|
||||||
self.logged = False
|
|
||||||
else:
|
|
||||||
self.logged = False
|
|
||||||
raise Exceptions.RequireCredentialsSessionError
|
|
||||||
|
|
||||||
def authorise(self):
|
|
||||||
""" Authorises a Twitter account. This function needs to be called for each new session, after self.get_configuration() and before self.login()"""
|
|
||||||
if self.logged == True:
|
|
||||||
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
|
||||||
auth = tweepy.OAuth1UserHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
|
||||||
redirect_url = auth.get_authorization_url()
|
|
||||||
webbrowser.open_new_tab(redirect_url)
|
|
||||||
verification_dialog = wx.TextEntryDialog(None, _("Enter your PIN code here"), _("Authorising account..."))
|
|
||||||
answer = verification_dialog.ShowModal()
|
|
||||||
code = verification_dialog.GetValue()
|
|
||||||
verification_dialog.Destroy()
|
|
||||||
if answer != wx.ID_OK:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
auth.get_access_token(code)
|
|
||||||
except TweepyException:
|
|
||||||
dlg = wx.MessageDialog(None, _("We could not authorice your Twitter account to be used in TWBlue. This might be caused due to an incorrect verification code. Please try to add the session again."), _("Authorization error"), wx.ICON_ERROR)
|
|
||||||
dlg.ShowModal()
|
|
||||||
dlg.Destroy()
|
|
||||||
return False
|
|
||||||
self.create_session_folder()
|
|
||||||
self.get_configuration()
|
|
||||||
self.settings["twitter"]["user_key"] = auth.access_token
|
|
||||||
self.settings["twitter"]["user_secret"] = auth.access_token_secret
|
|
||||||
self.settings.write()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def api_call(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs):
|
|
||||||
""" Make a call to the Twitter API. If there is a connectionError or another exception not related to Twitter, It will call the method again at least 25 times, waiting a while between calls. Useful for post methods.
|
|
||||||
If twitter returns an error, it will not call the method anymore.
|
|
||||||
call_name str: The method to call
|
|
||||||
action str: What you are doing on twitter, it will be reported to the user if report_success is set to True.
|
|
||||||
for example "following @tw_blue2" will be reported as "following @tw_blue2 succeeded".
|
|
||||||
_sound str: a sound to play if the call is executed properly.
|
|
||||||
report_success and report_failure bool: These are self explanatory. True or False.
|
|
||||||
preexec_message str: A message to speak to the user while the method is running, example: "trying to follow x user"."""
|
|
||||||
finished = False
|
|
||||||
tries = 0
|
|
||||||
if preexec_message:
|
|
||||||
output.speak(preexec_message, True)
|
|
||||||
while finished==False and tries < 25:
|
|
||||||
try:
|
|
||||||
val = getattr(self.twitter, call_name)(*args, **kwargs)
|
|
||||||
finished = True
|
|
||||||
except TweepyException as e:
|
|
||||||
output.speak(str(e))
|
|
||||||
val = None
|
|
||||||
if type(e) != NotFound and type(e) != Forbidden:
|
|
||||||
tries = tries+1
|
|
||||||
time.sleep(5)
|
|
||||||
elif report_failure:
|
|
||||||
output.speak(_("%s failed. Reason: %s") % (action, str(e)))
|
|
||||||
finished = True
|
|
||||||
# except:
|
|
||||||
# tries = tries + 1
|
|
||||||
# time.sleep(5)
|
|
||||||
if report_success:
|
|
||||||
output.speak(_("%s succeeded.") % action)
|
|
||||||
if _sound != None: self.sound.play(_sound)
|
|
||||||
return val
|
|
||||||
|
|
||||||
def api_call_v2(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs):
|
|
||||||
finished = False
|
|
||||||
tries = 0
|
|
||||||
if preexec_message:
|
|
||||||
output.speak(preexec_message, True)
|
|
||||||
while finished==False and tries < 25:
|
|
||||||
try:
|
|
||||||
val = getattr(self.twitter_v2, call_name)(*args, **kwargs)
|
|
||||||
finished = True
|
|
||||||
except TweepyException as e:
|
|
||||||
log.exception("Error sending the tweet.")
|
|
||||||
output.speak(str(e))
|
|
||||||
val = None
|
|
||||||
if type(e) != NotFound and type(e) != Forbidden:
|
|
||||||
tries = tries+1
|
|
||||||
time.sleep(5)
|
|
||||||
elif report_failure:
|
|
||||||
output.speak(_("%s failed. Reason: %s") % (action, str(e)))
|
|
||||||
finished = True
|
|
||||||
if report_success:
|
|
||||||
output.speak(_("%s succeeded.") % action)
|
|
||||||
if _sound != None: self.sound.play(_sound)
|
|
||||||
return val
|
|
||||||
|
|
||||||
def search(self, name, *args, **kwargs):
|
|
||||||
""" Search in twitter, passing args and kwargs as arguments to the Twython function."""
|
|
||||||
tl = self.twitter.search_tweets(*args, **kwargs)
|
|
||||||
tl.reverse()
|
|
||||||
return tl
|
|
||||||
|
|
||||||
# @_require_login
|
|
||||||
def get_favourites_timeline(self, name, *args, **kwargs):
|
|
||||||
""" Gets favourites for the authenticated user or a friend or follower.
|
|
||||||
name str: Name for storage in the database.
|
|
||||||
args and kwargs are passed directly to the Twython function."""
|
|
||||||
tl = self.call_paged("favorites", *args, **kwargs)
|
|
||||||
return self.order_buffer(name, tl)
|
|
||||||
|
|
||||||
def call_paged(self, update_function, name, *args, **kwargs):
|
|
||||||
""" Makes a call to the Twitter API methods several times. Useful for get methods.
|
|
||||||
this function is needed for retrieving more than 200 items.
|
|
||||||
update_function str: The function to call. This function must be child of self.twitter
|
|
||||||
args and kwargs are passed to update_function.
|
|
||||||
returns a list with all items retrieved."""
|
|
||||||
results = []
|
|
||||||
if self.db.get(name) == None or self.db.get(name) == []:
|
|
||||||
since_id = None
|
|
||||||
else:
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False:
|
|
||||||
since_id = self.db[name][-1].id
|
|
||||||
else:
|
|
||||||
since_id = self.db[name][0].id
|
|
||||||
data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], since_id=since_id, *args, **kwargs)
|
|
||||||
results.extend(data)
|
|
||||||
results.reverse()
|
|
||||||
return results
|
|
||||||
|
|
||||||
# @_require_login
|
|
||||||
def get_user_info(self):
|
|
||||||
""" Retrieves some information required by TWBlue for setup."""
|
|
||||||
offset = time.timezone if (time.localtime().tm_isdst == 0) else time.altzone
|
|
||||||
offset = offset*-1
|
|
||||||
self.db["utc_offset"] = offset
|
|
||||||
# Get twitter's supported languages and save them in a global variable
|
|
||||||
#so we won't call to this method once per session.
|
|
||||||
if len(application.supported_languages) == 0:
|
|
||||||
application.supported_languages = self.twitter.supported_languages()
|
|
||||||
self.get_lists()
|
|
||||||
self.get_muted_users()
|
|
||||||
self.settings.write()
|
|
||||||
|
|
||||||
# @_require_login
|
|
||||||
def get_lists(self):
|
|
||||||
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
|
|
||||||
self.db["lists"] = self.twitter.get_lists(reverse=True)
|
|
||||||
|
|
||||||
# @_require_login
|
|
||||||
def get_muted_users(self):
|
|
||||||
""" Gets muted users (oh really?)."""
|
|
||||||
self.db["muted_users"] = self.twitter.get_muted_ids()
|
|
||||||
|
|
||||||
# @_require_login
|
|
||||||
def get_stream(self, name, function, *args, **kwargs):
|
|
||||||
""" Retrieves the items for a regular stream.
|
|
||||||
name str: Name to save items to the database.
|
|
||||||
function str: A function to get the items."""
|
|
||||||
last_id = -1
|
|
||||||
if name in self.db:
|
|
||||||
try:
|
|
||||||
if self.db[name][0]["id"] > self.db[name][-1]["id"]:
|
|
||||||
last_id = self.db[name][0]["id"]
|
|
||||||
else:
|
|
||||||
last_id = self.db[name][-1]["id"]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
tl = self.call_paged(function, sinze_id=last_id, *args, **kwargs)
|
|
||||||
self.order_buffer(name, tl)
|
|
||||||
|
|
||||||
def get_cursored_stream(self, name, function, items="users", get_previous=False, *args, **kwargs):
|
|
||||||
""" Gets items for API calls that require using cursors to paginate the results.
|
|
||||||
name str: Name to save it in the database.
|
|
||||||
function str: Function that provides the items.
|
|
||||||
items: When the function returns the list with results, items will tell how the order function should be look. for example get_followers_list returns a list and users are under list["users"], here the items should point to "users".
|
|
||||||
get_previous bool: wether this function will be used to get previous items in a buffer or load the buffer from scratch.
|
|
||||||
returns number of items retrieved."""
|
|
||||||
items_ = []
|
|
||||||
try:
|
|
||||||
if "cursor" in self.db[name] and get_previous:
|
|
||||||
cursor = self.db[name]["cursor"]
|
|
||||||
else:
|
|
||||||
cursor = -1
|
|
||||||
except KeyError:
|
|
||||||
cursor = -1
|
|
||||||
if cursor != -1:
|
|
||||||
tl = getattr(self.twitter, function)(cursor=cursor, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
|
|
||||||
else:
|
|
||||||
tl = getattr(self.twitter, function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
|
|
||||||
tl[items].reverse()
|
|
||||||
num = self.order_cursored_buffer(name, tl[items])
|
|
||||||
# Recently, Twitter's new endpoints have cursor if there are more results.
|
|
||||||
if "next_cursor" in tl:
|
|
||||||
self.db[name]["cursor"] = tl["next_cursor"]
|
|
||||||
else:
|
|
||||||
self.db[name]["cursor"] = 0
|
|
||||||
return num
|
|
||||||
|
|
||||||
def check_connection(self):
|
|
||||||
""" Restart the Twitter object every 5 executions. It is useful for dealing with requests timeout and other oddities."""
|
|
||||||
log.debug("Executing check connection...")
|
|
||||||
self.counter += 1
|
|
||||||
if self.counter >= 4:
|
|
||||||
log.debug("Restarting connection after 5 minutes.")
|
|
||||||
del self.twitter
|
|
||||||
self.logged = False
|
|
||||||
self.login(False)
|
|
||||||
self.counter = 0
|
|
||||||
|
|
||||||
def check_quoted_status(self, tweet):
|
|
||||||
""" Helper for get_quoted_tweet. Get a quoted status inside a tweet and create a special tweet with all info available.
|
|
||||||
tweet dict: A tweet dictionary.
|
|
||||||
Returns a quoted tweet or the original tweet if is not a quote"""
|
|
||||||
status = tweets.is_long(tweet)
|
|
||||||
if status != False and config.app["app-settings"]["handle_longtweets"]:
|
|
||||||
quoted_tweet = self.get_quoted_tweet(tweet)
|
|
||||||
return quoted_tweet
|
|
||||||
return tweet
|
|
||||||
|
|
||||||
def get_quoted_tweet(self, tweet):
|
|
||||||
""" Process a tweet and extract all information related to the quote. """
|
|
||||||
quoted_tweet = tweet
|
|
||||||
if hasattr(tweet, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
if hasattr(quoted_tweet, "entities"):
|
|
||||||
setattr(quoted_tweet, value, utils.expand_urls(getattr(quoted_tweet, value), quoted_tweet.entities))
|
|
||||||
if hasattr(quoted_tweet, "is_quote_status") == True and hasattr(quoted_tweet, "quoted_status"):
|
|
||||||
original_tweet = quoted_tweet.quoted_status
|
|
||||||
elif hasattr(quoted_tweet, "retweeted_status") and hasattr(quoted_tweet.retweeted_status, "is_quote_status") == True and hasattr(quoted_tweet.retweeted_status, "quoted_status"):
|
|
||||||
original_tweet = quoted_tweet.retweeted_status.quoted_status
|
|
||||||
else:
|
|
||||||
return quoted_tweet
|
|
||||||
original_tweet = self.check_long_tweet(original_tweet)
|
|
||||||
if hasattr(original_tweet, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
elif hasattr(original_tweet, "message"):
|
|
||||||
value = "message"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
if hasattr(original_tweet, "entities"):
|
|
||||||
setattr(original_tweet, value, utils.expand_urls(getattr(original_tweet, value), original_tweet.entities))
|
|
||||||
# ToDo: Shall we check whether we should add show_screen_names here?
|
|
||||||
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, session=self)
|
|
||||||
|
|
||||||
def check_long_tweet(self, tweet):
|
|
||||||
""" Process a tweet and add extra info if it's a long tweet made with Twyshort.
|
|
||||||
tweet dict: a tweet object.
|
|
||||||
returns a tweet with a new argument message, or original tweet if it's not a long tweet."""
|
|
||||||
long = False
|
|
||||||
if hasattr(tweet, "entities") and tweet.entities.get("urls"):
|
|
||||||
long = twishort.is_long(tweet)
|
|
||||||
if long != False and config.app["app-settings"]["handle_longtweets"]:
|
|
||||||
message = twishort.get_full_text(long)
|
|
||||||
if hasattr(tweet, "quoted_status"):
|
|
||||||
tweet.quoted_status.message = message
|
|
||||||
if tweet.quoted_status.message == False: return False
|
|
||||||
tweet.quoted_status.twishort = True
|
|
||||||
if hasattr(tweet.quoted_status, "entities") and tweet.quoted_status.entities.get("user_mentions"):
|
|
||||||
for i in tweet.quoted_status.entities["user_mentions"]:
|
|
||||||
if "@%s" % (i["screen_name"]) not in tweet.quoted_status.message and i["screen_name"] != self.get_user(tweet.user).screen_name:
|
|
||||||
if hasattr(tweet.quoted_status, "retweeted_status") and self.get_user(tweet.retweeted_status.user).screen_name == i["screen_name"]:
|
|
||||||
continue
|
|
||||||
tweet.quoted_status.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
|
||||||
else:
|
|
||||||
tweet.message = message
|
|
||||||
if tweet.message == False: return False
|
|
||||||
tweet.twishort = True
|
|
||||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
|
||||||
for i in tweet.entities["user_mentions"]:
|
|
||||||
if "@%s" % (i["screen_name"]) not in tweet.message and i["screen_name"] != self.get_user(tweet.user).screen_name:
|
|
||||||
if hasattr(tweet, "retweeted_status") and self.get_user(tweet.retweeted_status.user).screen_name == i["screen_name"]:
|
|
||||||
continue
|
|
||||||
tweet.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
|
||||||
return tweet
|
|
||||||
|
|
||||||
def get_user(self, id):
|
|
||||||
""" Returns an user object associated with an ID.
|
|
||||||
id str: User identifier, provided by Twitter.
|
|
||||||
returns a tweepy user object."""
|
|
||||||
if hasattr(id, "id_str"):
|
|
||||||
log.error("Called get_user function by passing a full user id as a parameter.")
|
|
||||||
id = id.id_str
|
|
||||||
# Check if the user has been added to the list of deleted users previously.
|
|
||||||
if id in self.deleted_users:
|
|
||||||
log.debug("Returning user {} from the list of deleted users.".format(id))
|
|
||||||
return self.deleted_users[id]
|
|
||||||
if ("users" in self.db) == False or (str(id) in self.db["users"]) == False:
|
|
||||||
log.debug("Requesting user id {} as it is not present in the users database.".format(id))
|
|
||||||
try:
|
|
||||||
user = self.twitter.get_user(id=id)
|
|
||||||
except TweepyException as err:
|
|
||||||
user = UserModel(None)
|
|
||||||
user.screen_name = "deleted_user"
|
|
||||||
user.id = id
|
|
||||||
user.name = _("Deleted account")
|
|
||||||
if type(err) == NotFound:
|
|
||||||
self.deleted_users[id] = user
|
|
||||||
return user
|
|
||||||
else:
|
|
||||||
log.exception("Error when attempting to retrieve an user from Twitter.")
|
|
||||||
return user
|
|
||||||
users = self.db["users"]
|
|
||||||
users[user.id_str] = user
|
|
||||||
self.db["users"] = users
|
|
||||||
user.name = self.get_user_alias(user)
|
|
||||||
return user
|
|
||||||
else:
|
|
||||||
user = self.db["users"][str(id)]
|
|
||||||
user.name = self.get_user_alias(user)
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_user_alias(self, user):
|
|
||||||
""" Retrieves an alias for the passed user model, if exists.
|
|
||||||
@ user Tweepy.models.user: An user object.
|
|
||||||
"""
|
|
||||||
aliases = self.settings.get("user-aliases")
|
|
||||||
if aliases == None:
|
|
||||||
log.error("Aliases are not defined for this config spec.")
|
|
||||||
return self.demoji_user(user.name)
|
|
||||||
user_alias = aliases.get(user.id_str)
|
|
||||||
if user_alias != None:
|
|
||||||
return user_alias
|
|
||||||
return self.demoji_user(user.name)
|
|
||||||
|
|
||||||
def demoji_user(self, name):
|
|
||||||
if self.settings["general"]["hide_emojis"] == True:
|
|
||||||
return demoji.replace(name, "")
|
|
||||||
return name
|
|
||||||
|
|
||||||
def get_user_by_screen_name(self, screen_name):
|
|
||||||
""" Returns an user identifier associated with a screen_name.
|
|
||||||
screen_name str: User name, such as tw_blue2, provided by Twitter.
|
|
||||||
returns an user ID."""
|
|
||||||
if ("users" in self.db) == False:
|
|
||||||
user = utils.if_user_exists(self.twitter, screen_name)
|
|
||||||
users = self.db["users"]
|
|
||||||
users[user["id"]] = user
|
|
||||||
self.db["users"] = users
|
|
||||||
return user["id"]
|
|
||||||
else:
|
|
||||||
for i in list(self.db["users"].keys()):
|
|
||||||
if self.db["users"][i].screen_name == screen_name:
|
|
||||||
return self.db["users"][i].id
|
|
||||||
user = utils.if_user_exists(self.twitter, screen_name)
|
|
||||||
users = self.db["users"]
|
|
||||||
users[user.id] = user
|
|
||||||
self.db["users"] = users
|
|
||||||
return user.id
|
|
||||||
|
|
||||||
def save_users(self, user_ids):
|
|
||||||
""" Adds all new users to the users database. """
|
|
||||||
if len(user_ids) == 0:
|
|
||||||
return
|
|
||||||
log.debug("Received %d user IDS to be added in the database." % (len(user_ids)))
|
|
||||||
users_to_retrieve = [user_id for user_id in user_ids if (user_id not in self.db["users"] and user_id not in self.deleted_users)]
|
|
||||||
# Remove duplicates
|
|
||||||
users_to_retrieve = list(dict.fromkeys(users_to_retrieve))
|
|
||||||
if len(users_to_retrieve) == 0:
|
|
||||||
return
|
|
||||||
log.debug("TWBlue will get %d new users from Twitter." % (len(users_to_retrieve)))
|
|
||||||
try:
|
|
||||||
users = self.twitter.lookup_users(user_id=users_to_retrieve, tweet_mode="extended")
|
|
||||||
users_db = self.db["users"]
|
|
||||||
for user in users:
|
|
||||||
users_db[user.id_str] = user
|
|
||||||
log.debug("Added %d new users" % (len(users)))
|
|
||||||
self.db["users"] = users_db
|
|
||||||
except TweepyException as err:
|
|
||||||
if type(err) == NotFound: # User not found.
|
|
||||||
log.error("The specified users {} were not found in twitter.".format(user_ids))
|
|
||||||
# Creates a deleted user object for every user_id not found here.
|
|
||||||
# This will make TWBlue to not waste Twitter API calls when attempting to retrieve those users again.
|
|
||||||
# As deleted_users is not saved across restarts, when restarting TWBlue, it will retrieve the correct users if they enabled their accounts.
|
|
||||||
for id in users_to_retrieve:
|
|
||||||
user = UserModel(None)
|
|
||||||
user.screen_name = "deleted_user"
|
|
||||||
user.id = id
|
|
||||||
user.name = _("Deleted account")
|
|
||||||
self.deleted_users[id] = user
|
|
||||||
else:
|
|
||||||
log.exception("An exception happened while attempting to retrieve a list of users from direct messages in Twitter.")
|
|
||||||
|
|
||||||
def add_users_from_results(self, data):
|
|
||||||
users = self.db["users"]
|
|
||||||
for i in data:
|
|
||||||
if hasattr(i, "user"):
|
|
||||||
if isinstance(i.user, str):
|
|
||||||
log.warning("A String was passed to be added as an user. This is normal only if TWBlue tried to load a conversation.")
|
|
||||||
continue
|
|
||||||
if (i.user.id_str in self.db["users"]) == False:
|
|
||||||
users[i.user.id_str] = i.user
|
|
||||||
if hasattr(i, "quoted_status") and (i.quoted_status.user.id_str in self.db["users"]) == False:
|
|
||||||
users[i.quoted_status.user.id_str] = i.quoted_status.user
|
|
||||||
|
|
||||||
if hasattr(i, "retweeted_status") and (i.retweeted_status.user.id_str in self.db["users"]) == False:
|
|
||||||
users[i.retweeted_status.user.id_str] = i.retweeted_status.user
|
|
||||||
self.db["users"] = users
|
|
||||||
|
|
||||||
def start_streaming(self):
|
|
||||||
if config.app["app-settings"]["no_streaming"]:
|
|
||||||
return
|
|
||||||
self.stream = streaming.Stream(twitter_api=self.twitter, session_name=self.get_name(), user_id=self.db["user_id"], muted_users=self.db["muted_users"], consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"], chunk_size=1025)
|
|
||||||
self.stream_thread = call_threaded(self.stream.filter, follow=self.stream.users, stall_warnings=True)
|
|
||||||
|
|
||||||
def stop_streaming(self):
|
|
||||||
if config.app["app-settings"]["no_streaming"]:
|
|
||||||
return
|
|
||||||
if hasattr(self, "stream"):
|
|
||||||
self.stream.running = False
|
|
||||||
log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
|
||||||
|
|
||||||
def handle_new_status(self, status, session_name):
|
|
||||||
""" 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.get_name() != session_name:
|
|
||||||
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, "quoted_status") and status.retweeted_status.quoted_status.truncated:
|
|
||||||
status.retweeted_status.quoted_status._json = {**status.retweeted_status.quoted_status._json, **status.retweeted_status.quoted_status._json["extended_tweet"]}
|
|
||||||
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"]
|
|
||||||
elif hasattr(status, "quoted_status") and hasattr(status.quoted_status, "extended_tweet"):
|
|
||||||
status.quoted_status._json = {**status.quoted_status._json, **status.quoted_status._json["extended_tweet"]}
|
|
||||||
if status.truncated:
|
|
||||||
status._json = {**status._json, **status._json["extended_tweet"]}
|
|
||||||
# Sends status to database, where it will be reduced and changed according to our needs.
|
|
||||||
buffers_to_send = []
|
|
||||||
if status.user.id_str in self.stream.users:
|
|
||||||
buffers_to_send.append("home_timeline")
|
|
||||||
if status.user.id == self.db["user_id"]:
|
|
||||||
buffers_to_send.append("sent_tweets")
|
|
||||||
for user in status.entities["user_mentions"]:
|
|
||||||
if user["id"] == self.db["user_id"]:
|
|
||||||
buffers_to_send.append("mentions")
|
|
||||||
users_with_timeline = [user.split("-")[0] for user in self.db.keys() if user.endswith("-timeline")]
|
|
||||||
for user in users_with_timeline:
|
|
||||||
if status.user.id_str == user:
|
|
||||||
buffers_to_send.append("{}-timeline".format(user))
|
|
||||||
for buffer in buffers_to_send[::]:
|
|
||||||
num = self.order_buffer(buffer, [status])
|
|
||||||
if num == 0:
|
|
||||||
buffers_to_send.remove(buffer)
|
|
||||||
# However, we have to do the "reduce and change" process here because the status we sent to the db is going to be a different object that the one sent to controller.
|
|
||||||
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("twitter.new_tweet", data=status, session_name=self.get_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, session_name):
|
|
||||||
if self.logged == False:
|
|
||||||
return
|
|
||||||
if session_name != self.get_name():
|
|
||||||
log.debug("Connected streaming endpoint on session {}".format(session_name))
|
|
||||||
|
|
||||||
def send_tweet(self, *tweets):
|
|
||||||
""" Convenience function to send a thread. """
|
|
||||||
in_reply_to_status_id = None
|
|
||||||
for obj in tweets:
|
|
||||||
# When quoting a tweet, the tweet_data dict might contain a parameter called quote_tweet_id. Let's add it, or None, so quotes will be posted successfully.
|
|
||||||
if len(obj["attachments"]) == 0:
|
|
||||||
item = self.api_call_v2(call_name="create_tweet", text=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"], quote_tweet_id=obj.get("quote_tweet_id"))
|
|
||||||
in_reply_to_status_id = item.data["id"]
|
|
||||||
else:
|
|
||||||
media_ids = []
|
|
||||||
for i in obj["attachments"]:
|
|
||||||
img = self.api_call("media_upload", filename=i["file"])
|
|
||||||
if i["type"] == "photo":
|
|
||||||
self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"])
|
|
||||||
media_ids.append(img.media_id)
|
|
||||||
item = self.api_call_v2(call_name="create_tweet", text=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, media_ids=media_ids, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"], quote_tweet_id=obj.get("quote_tweet_id"))
|
|
||||||
in_reply_to_status_id = item.data["id"]
|
|
||||||
|
|
||||||
def reply(self, text="", in_reply_to_status_id=None, attachments=[], *args, **kwargs):
|
|
||||||
if len(attachments) == 0:
|
|
||||||
item = self.api_call_v2(call_name="create_tweet", text=text, _sound="reply_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, *args, **kwargs)
|
|
||||||
else:
|
|
||||||
media_ids = []
|
|
||||||
for i in attachments:
|
|
||||||
img = self.api_call("media_upload", filename=i["file"])
|
|
||||||
if i["type"] == "photo":
|
|
||||||
self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"])
|
|
||||||
media_ids.append(img.media_id)
|
|
||||||
item = self.api_call_v2(call_name="create_tweet", text=text, _sound="reply_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, media_ids=media_ids, *args, **kwargs)
|
|
||||||
|
|
||||||
def direct_message(self, text, recipient, attachment=None, *args, **kwargs):
|
|
||||||
if attachment == None:
|
|
||||||
item = self.api_call(call_name="send_direct_message", recipient_id=recipient, text=text)
|
|
||||||
else:
|
|
||||||
if attachment["type"] == "photo":
|
|
||||||
media_category = "DmImage"
|
|
||||||
elif attachment["type"] == "gif":
|
|
||||||
media_category = "DmGif"
|
|
||||||
elif attachment["type"] == "video":
|
|
||||||
media_category = "DmVideo"
|
|
||||||
media = self.api_call("media_upload", filename=attachment["file"], media_category=media_category)
|
|
||||||
item = self.api_call(call_name="send_direct_message", recipient_id=recipient, text=text, attachment_type="media", attachment_media_id=media.media_id)
|
|
||||||
if item != None:
|
|
||||||
sent_dms = self.db["sent_direct_messages"]
|
|
||||||
if self.settings["general"]["reverse_timelines"] == False:
|
|
||||||
sent_dms.append(item)
|
|
||||||
else:
|
|
||||||
sent_dms.insert(0, item)
|
|
||||||
self.db["sent_direct_messages"] = sent_dms
|
|
||||||
pub.sendMessage("twitter.sent_dm", data=item, session_name=self.get_name())
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
if self.logged:
|
|
||||||
return "Twitter: {}".format(self.db["user_name"])
|
|
||||||
else:
|
|
||||||
return "Twitter: {}".format(self.settings["twitter"]["user_name"])
|
|
@ -1,47 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Streaming support for TWBlue. """
|
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
import six
|
|
||||||
import requests
|
|
||||||
import urllib3
|
|
||||||
import ssl
|
|
||||||
import tweepy
|
|
||||||
import logging
|
|
||||||
from pubsub import pub
|
|
||||||
|
|
||||||
log = logging.getLogger("sessions.twitter.streaming")
|
|
||||||
|
|
||||||
class Stream(tweepy.Stream):
|
|
||||||
|
|
||||||
def __init__(self, twitter_api, session_name, user_id, muted_users=[], *args, **kwargs):
|
|
||||||
super(Stream, self).__init__(*args, **kwargs)
|
|
||||||
log.debug("Starting streaming listener for session {}".format(session_name))
|
|
||||||
self.started = False
|
|
||||||
self.users = []
|
|
||||||
self.api = twitter_api
|
|
||||||
self.session_name = session_name
|
|
||||||
self.user_id = user_id
|
|
||||||
friends = self.api.get_friend_ids()
|
|
||||||
log.debug("Retrieved {} friends to add to the streaming listener.".format(len(friends)))
|
|
||||||
self.users.append(str(self.user_id))
|
|
||||||
log.debug("Got {} muted users.".format(len(muted_users)))
|
|
||||||
for user in friends:
|
|
||||||
if user not in muted_users:
|
|
||||||
self.users.append(str(user))
|
|
||||||
self.started = True
|
|
||||||
log.debug("Streaming listener started with {} users to follow.".format(len(self.users)))
|
|
||||||
|
|
||||||
def on_connect(self):
|
|
||||||
pub.sendMessage("twitter.stream_connected", session_name=self.session_name)
|
|
||||||
|
|
||||||
def on_exception(self, ex):
|
|
||||||
log.exception("Exception received on streaming endpoint for session {}".format(self.session_name))
|
|
||||||
|
|
||||||
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.id != self.user_id:
|
|
||||||
return
|
|
||||||
if status.user.id_str in self.users:
|
|
||||||
pub.sendMessage("twitter.new_status", status=status, session_name=self.session_name)
|
|
@ -1,152 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import re
|
|
||||||
import arrow
|
|
||||||
import languageHandler
|
|
||||||
from string import Template
|
|
||||||
from . import utils
|
|
||||||
|
|
||||||
# Define variables that would be available for all template objects.
|
|
||||||
# This will be used for the edit template dialog.
|
|
||||||
# Available variables for tweet objects.
|
|
||||||
tweet_variables = ["date", "display_name", "screen_name", "source", "lang", "text", "image_descriptions"]
|
|
||||||
dm_variables = ["date", "sender_display_name", "sender_screen_name", "recipient_display_name", "recipient_display_name", "text"]
|
|
||||||
person_variables = ["display_name", "screen_name", "location", "description", "followers", "following", "listed", "likes", "tweets", "created_at"]
|
|
||||||
|
|
||||||
# Default, translatable templates.
|
|
||||||
tweet_default_template = _("$display_name, $text $image_descriptions $date. $source")
|
|
||||||
dm_default_template = _("$sender_display_name, $text $date")
|
|
||||||
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
|
|
||||||
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.")
|
|
||||||
|
|
||||||
def process_date(field, relative_times=True, offset_seconds=0):
|
|
||||||
original_date = arrow.get(field, locale="en")
|
|
||||||
if relative_times == True:
|
|
||||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts = original_date.shift(seconds=offset_seconds).format(_("dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
|
||||||
return ts
|
|
||||||
|
|
||||||
def process_text(tweet):
|
|
||||||
if hasattr(tweet, "full_text"):
|
|
||||||
text = tweet.full_text
|
|
||||||
elif hasattr(tweet, "text"):
|
|
||||||
text = tweet.text
|
|
||||||
# Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read.
|
|
||||||
text = utils.clean_mentions(utils.StripChars(text))
|
|
||||||
# Replace URLS for extended version of those.
|
|
||||||
if hasattr(tweet, "entities"):
|
|
||||||
text = utils.expand_urls(text, tweet.entities)
|
|
||||||
text = re.sub(r"https://twitter.com/\w+/status/\S+", "", text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
def process_image_descriptions(entities):
|
|
||||||
""" Attempt to extract information for image descriptions. """
|
|
||||||
image_descriptions = []
|
|
||||||
for media in entities["media"]:
|
|
||||||
if media.get("ext_alt_text") != None:
|
|
||||||
image_descriptions.append(media.get("ext_alt_text"))
|
|
||||||
# Tweets retrieved via the Streaming API have a description field in media photos with image description available.
|
|
||||||
elif media.get("description") != None:
|
|
||||||
image_descriptions.append(media.get("description"))
|
|
||||||
|
|
||||||
idescriptions = ""
|
|
||||||
for image in image_descriptions:
|
|
||||||
idescriptions += _("Image description: {}.").format(image)
|
|
||||||
return idescriptions
|
|
||||||
|
|
||||||
def render_tweet(tweet, template, session, relative_times=False, offset_seconds=0):
|
|
||||||
""" Renders any given Tweet according to the passed template.
|
|
||||||
Available data for tweets will be stored in the following variables:
|
|
||||||
$date: Creation date.
|
|
||||||
$display_name: User profile name.
|
|
||||||
$screen_name: User screen name, this is the same name used to reference the user in Twitter.
|
|
||||||
$ source: Source client from where the current tweet was sent.
|
|
||||||
$lang: Two letter code for the automatically detected language for the tweet. This detection is performed by Twitter.
|
|
||||||
$text: Tweet text.
|
|
||||||
$image_descriptions: Information regarding image descriptions added by twitter users.
|
|
||||||
"""
|
|
||||||
global tweet_variables
|
|
||||||
available_data = dict()
|
|
||||||
created_at = process_date(tweet.created_at, relative_times, offset_seconds)
|
|
||||||
available_data.update(date=created_at)
|
|
||||||
# user.
|
|
||||||
available_data.update(display_name=session.get_user(tweet.user).name, screen_name=session.get_user(tweet.user).screen_name)
|
|
||||||
# Source client from where tweet was originated.
|
|
||||||
available_data.update(source=tweet.source)
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
if hasattr(tweet.retweeted_status, "quoted_status"):
|
|
||||||
text = _("RT @{}: {} Quote from @{}: {}").format(session.get_user(tweet.retweeted_status.user).screen_name, process_text(tweet.retweeted_status), session.get_user(tweet.retweeted_status.quoted_status.user).screen_name, process_text(tweet.retweeted_status.quoted_status))
|
|
||||||
else:
|
|
||||||
text = _("RT @{}: {}").format(session.get_user(tweet.retweeted_status.user).screen_name, process_text(tweet.retweeted_status))
|
|
||||||
elif hasattr(tweet, "quoted_status"):
|
|
||||||
text = _("{} Quote from @{}: {}").format(process_text(tweet), session.get_user(tweet.quoted_status.user).screen_name, process_text(tweet.quoted_status))
|
|
||||||
else:
|
|
||||||
text = process_text(tweet)
|
|
||||||
available_data.update(lang=tweet.lang, text=text)
|
|
||||||
# process image descriptions
|
|
||||||
image_descriptions = ""
|
|
||||||
if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"):
|
|
||||||
image_descriptions = process_image_descriptions(tweet.quoted_status.extended_entities)
|
|
||||||
elif hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "quoted_status") and hasattr(tweet.retweeted_status.quoted_status, "extended_entities"):
|
|
||||||
image_descriptions = process_image_descriptions(tweet.retweeted_status.quoted_status.extended_entities)
|
|
||||||
elif hasattr(tweet, "extended_entities"):
|
|
||||||
image_descriptions = process_image_descriptions(tweet.extended_entities)
|
|
||||||
available_data.update(image_descriptions=image_descriptions)
|
|
||||||
result = Template(_(template)).safe_substitute(**available_data)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def render_dm(dm, template, session, relative_times=False, offset_seconds=0):
|
|
||||||
""" Renders direct messages by using the provided template.
|
|
||||||
Available data will be stored in the following variables:
|
|
||||||
$date: Creation date.
|
|
||||||
$sender_display_name: User profile name for user who sent the dm.
|
|
||||||
$sender_screen_name: User screen name for user sending the dm, this is the same name used to reference the user in Twitter.
|
|
||||||
$recipient_display_name: User profile name for user who received the dm.
|
|
||||||
$recipient_screen_name: User screen name for user receiving the dm, this is the same name used to reference the user in Twitter.
|
|
||||||
$text: Text of the direct message.
|
|
||||||
"""
|
|
||||||
global dm_variables
|
|
||||||
available_data = dict()
|
|
||||||
available_data.update(text=utils.expand_urls(utils.StripChars(dm.message_create["message_data"]["text"]), dm.message_create["message_data"]["entities"]))
|
|
||||||
# Let's remove the last 3 digits in the timestamp string.
|
|
||||||
# Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it.
|
|
||||||
original_date = arrow.get(int(dm.created_timestamp))
|
|
||||||
if relative_times == True:
|
|
||||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
|
||||||
else:
|
|
||||||
ts = original_date.shift(seconds=offset_seconds)
|
|
||||||
available_data.update(date=ts)
|
|
||||||
sender = session.get_user(dm.message_create["sender_id"])
|
|
||||||
recipient = session.get_user(dm.message_create["target"]["recipient_id"])
|
|
||||||
available_data.update(sender_display_name=sender.name, sender_screen_name=sender.screen_name, recipient_display_name=recipient.name, recipient_screen_name=recipient.screen_name)
|
|
||||||
result = Template(_(template)).safe_substitute(**available_data)
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Sesion object is not used in this function but we keep compatibility across all rendering functions.
|
|
||||||
def render_person(user, template, session=None, relative_times=True, offset_seconds=0):
|
|
||||||
""" Renders persons (any Twitter user) by using the provided template.
|
|
||||||
Available data will be stored in the following variables:
|
|
||||||
$display_name: The name of the user, as they’ve defined it. Not necessarily a person’s name. Typically capped at 50 characters, but subject to change.
|
|
||||||
$screen_name: The screen name, handle, or alias that this user identifies themselves with.
|
|
||||||
$location: The user-defined location for this account’s profile. Not necessarily a location, nor machine-parseable.
|
|
||||||
$description: The user-defined UTF-8 string describing their account.
|
|
||||||
$followers: The number of followers this account currently has. This value might be inaccurate.
|
|
||||||
$following: The number of users this account is following (AKA their “followings”). This value might be inaccurate.
|
|
||||||
$listed: The number of public lists that this user is a member of. This value might be inaccurate.
|
|
||||||
$likes: The number of Tweets this user has liked in the account’s lifetime. This value might be inaccurate.
|
|
||||||
$tweets: The number of Tweets (including retweets) issued by the user. This value might be inaccurate.
|
|
||||||
$created_at: The date and time that the user account was created on Twitter.
|
|
||||||
"""
|
|
||||||
global person_variables
|
|
||||||
available_data = dict(display_name=user.name, screen_name=user.screen_name, followers=user.followers_count, following=user.friends_count, likes=user.favourites_count, listed=user.listed_count, tweets=user.statuses_count)
|
|
||||||
# Nullable values.
|
|
||||||
nullables = ["location", "description"]
|
|
||||||
for nullable in nullables:
|
|
||||||
if hasattr(user, nullable) and getattr(user, nullable) != None:
|
|
||||||
available_data[nullable] = getattr(user, nullable)
|
|
||||||
else:
|
|
||||||
available_data[nullable] = ""
|
|
||||||
created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds)
|
|
||||||
available_data.update(created_at=created_at)
|
|
||||||
result = Template(_(template)).safe_substitute(**available_data)
|
|
||||||
return result
|
|
@ -1,275 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import re
|
|
||||||
import html.entities
|
|
||||||
import output
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import time
|
|
||||||
from tweepy.errors import TweepyException, NotFound, Forbidden
|
|
||||||
log = logging.getLogger("twitter.utils")
|
|
||||||
""" Some utilities for the twitter interface."""
|
|
||||||
|
|
||||||
__version__ = 0.1
|
|
||||||
__doc__ = "Find urls in tweets and #audio hashtag."
|
|
||||||
|
|
||||||
url_re = re.compile(r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))")
|
|
||||||
|
|
||||||
url_re2 = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ \\n\\t]*")
|
|
||||||
bad_chars = '\'\\\n.,[](){}:;"'
|
|
||||||
|
|
||||||
def StripChars(s):
|
|
||||||
"""Converts any html entities in s to their unicode-decoded equivalents and returns a string."""
|
|
||||||
entity_re = re.compile(r"&(#\d+|\w+);")
|
|
||||||
def matchFunc(match):
|
|
||||||
"""Nested function to handle a match object.
|
|
||||||
If we match &blah; and it's not found, &blah; will be returned.
|
|
||||||
if we match #\d+, unichr(digits) will be returned.
|
|
||||||
Else, a unicode string will be returned."""
|
|
||||||
if match.group(1).startswith('#'): return chr(int(match.group(1)[1:]))
|
|
||||||
replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1))
|
|
||||||
return replacement
|
|
||||||
return str(entity_re.sub(matchFunc, s))
|
|
||||||
|
|
||||||
def find_urls_in_text(text):
|
|
||||||
return url_re2.findall(text)
|
|
||||||
|
|
||||||
def find_urls (tweet, twitter_media=False):
|
|
||||||
urls = []
|
|
||||||
if twitter_media and hasattr(tweet, "extended_entities"):
|
|
||||||
for mediaItem in tweet.extended_entities["media"]:
|
|
||||||
if mediaItem["type"] == "video":
|
|
||||||
for variant in mediaItem["video_info"]["variants"]:
|
|
||||||
if variant["content_type"] == "video/mp4":
|
|
||||||
urls.append(variant["url"])
|
|
||||||
break
|
|
||||||
# Let's add URLS from tweet entities.
|
|
||||||
if hasattr(tweet, "message_create"):
|
|
||||||
entities = tweet.message_create["message_data"]["entities"]
|
|
||||||
else:
|
|
||||||
if hasattr(tweet, "entities") == True:
|
|
||||||
entities = tweet.entities
|
|
||||||
if entities.get("urls") != None:
|
|
||||||
for i in entities["urls"]:
|
|
||||||
if i["expanded_url"] not in urls:
|
|
||||||
urls.append(i["expanded_url"])
|
|
||||||
if hasattr(tweet, "quoted_status"):
|
|
||||||
urls.extend(find_urls(tweet.quoted_status, twitter_media))
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
urls.extend(find_urls(tweet.retweeted_status, twitter_media))
|
|
||||||
if hasattr(tweet, "message"):
|
|
||||||
i = "message"
|
|
||||||
elif hasattr(tweet, "full_text"):
|
|
||||||
i = "full_text"
|
|
||||||
else:
|
|
||||||
i = "text"
|
|
||||||
if hasattr(tweet, "message_create"):
|
|
||||||
extracted_urls = find_urls_in_text(tweet.message_create["message_data"]["text"])
|
|
||||||
else:
|
|
||||||
extracted_urls = find_urls_in_text(getattr(tweet, i))
|
|
||||||
# Don't include t.co links (mostly they are photos or shortened versions of already added URLS).
|
|
||||||
for i in extracted_urls:
|
|
||||||
if i not in urls and "https://t.co" not in i:
|
|
||||||
urls.append(i)
|
|
||||||
return urls
|
|
||||||
|
|
||||||
def find_item(item, listItems):
|
|
||||||
for i in range(0, len(listItems)):
|
|
||||||
if listItems[i].id == item.id:
|
|
||||||
return i
|
|
||||||
# Check also retweets.
|
|
||||||
if hasattr(item, "retweeted_status") and item.retweeted_status.id == listItems[i].id:
|
|
||||||
return i
|
|
||||||
return None
|
|
||||||
|
|
||||||
def find_list(name, lists):
|
|
||||||
for i in range(0, len(lists)):
|
|
||||||
if lists[i].name == name: return lists[i].id
|
|
||||||
|
|
||||||
def is_audio(tweet):
|
|
||||||
if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"):
|
|
||||||
result = is_audio(tweet.quoted_status)
|
|
||||||
if result != None:
|
|
||||||
return result
|
|
||||||
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities"):
|
|
||||||
result = is_audio(tweet.retweeted_status)
|
|
||||||
if result == True:
|
|
||||||
return result
|
|
||||||
# Checks firstly for Twitter videos and audios.
|
|
||||||
if hasattr(tweet, "extended_entities"):
|
|
||||||
for mediaItem in tweet.extended_entities["media"]:
|
|
||||||
if mediaItem["type"] == "video":
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
if len(find_urls(tweet)) < 1:
|
|
||||||
return False
|
|
||||||
if hasattr(tweet, "message_create"):
|
|
||||||
entities = tweet.message_create["message_data"]["entities"]
|
|
||||||
else:
|
|
||||||
if hasattr(tweet, "entities") == False or tweet.entities.get("hashtags") == None:
|
|
||||||
return False
|
|
||||||
entities = tweet.entities
|
|
||||||
if len(entities["hashtags"]) > 0:
|
|
||||||
for i in entities["hashtags"]:
|
|
||||||
if i["text"] == "audio":
|
|
||||||
return True
|
|
||||||
except IndexError:
|
|
||||||
log.exception("Exception while executing is_audio hashtag algorithm")
|
|
||||||
|
|
||||||
def is_geocoded(tweet):
|
|
||||||
if hasattr(tweet, "coordinates") and tweet.coordinates != None:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_media(tweet):
|
|
||||||
if hasattr(tweet, "message_create"):
|
|
||||||
entities = tweet.message_create["message_data"]["entities"]
|
|
||||||
else:
|
|
||||||
if hasattr(tweet, "entities") == False or tweet.entities.get("hashtags") == None:
|
|
||||||
return False
|
|
||||||
entities = tweet.entities
|
|
||||||
if entities.get("media") == None:
|
|
||||||
return False
|
|
||||||
for i in entities["media"]:
|
|
||||||
if i.get("type") != None and i.get("type") == "photo":
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_all_mentioned(tweet, conf, field="screen_name"):
|
|
||||||
""" Gets all users that have been mentioned."""
|
|
||||||
results = []
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
results.extend(get_all_mentioned(tweet.retweeted_status, conf, field))
|
|
||||||
if hasattr(tweet, "quoted_status"):
|
|
||||||
results.extend(get_all_mentioned(tweet.quoted_status, conf, field))
|
|
||||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
|
||||||
for i in tweet.entities["user_mentions"]:
|
|
||||||
if i["screen_name"] != conf["user_name"] and i["id_str"] != tweet.user:
|
|
||||||
if i.get(field) not in results:
|
|
||||||
results.append(i.get(field))
|
|
||||||
return results
|
|
||||||
|
|
||||||
def get_all_users(tweet, session):
|
|
||||||
string = []
|
|
||||||
user = session.get_user(tweet.user)
|
|
||||||
if user.screen_name != session.db["user_name"]:
|
|
||||||
string.append(user.screen_name)
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
string.extend(get_all_users(tweet.retweeted_status, session))
|
|
||||||
if hasattr(tweet, "quoted_status"):
|
|
||||||
string.extend(get_all_users(tweet.quoted_status, session))
|
|
||||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
|
||||||
for i in tweet.entities["user_mentions"]:
|
|
||||||
if i["screen_name"] != session.db["user_name"] and i["screen_name"] != user.screen_name:
|
|
||||||
if i["screen_name"] not in string:
|
|
||||||
string.append(i["screen_name"])
|
|
||||||
# Attempt to remove duplicates, tipically caused by nested tweets.
|
|
||||||
string = list(dict.fromkeys(string))
|
|
||||||
if len(string) == 0:
|
|
||||||
string.append(user.screen_name)
|
|
||||||
return string
|
|
||||||
|
|
||||||
def if_user_exists(twitter, user):
|
|
||||||
try:
|
|
||||||
data = twitter.get_user(screen_name=user)
|
|
||||||
return data
|
|
||||||
except TweepyException as err:
|
|
||||||
if type(err) == NotFound:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return user
|
|
||||||
|
|
||||||
def is_allowed(tweet, settings, buffer_name):
|
|
||||||
clients = settings["twitter"]["ignored_clients"]
|
|
||||||
if hasattr(tweet, "sender"): return True
|
|
||||||
allowed = True
|
|
||||||
tweet_data = {}
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
tweet_data["retweet"] = True
|
|
||||||
if hasattr(tweet, "in_reply_to_status_id"):
|
|
||||||
tweet_data["reply"] = True
|
|
||||||
if hasattr(tweet, "quoted_status"):
|
|
||||||
tweet_data["quote"] = True
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
|
||||||
tweet = tweet.retweeted_status
|
|
||||||
source = tweet.source
|
|
||||||
for i in clients:
|
|
||||||
if i.lower() == source.lower():
|
|
||||||
return False
|
|
||||||
return filter_tweet(tweet, tweet_data, settings, buffer_name)
|
|
||||||
|
|
||||||
def filter_tweet(tweet, tweet_data, settings, buffer_name):
|
|
||||||
if hasattr(tweet, "full_text"):
|
|
||||||
value = "full_text"
|
|
||||||
else:
|
|
||||||
value = "text"
|
|
||||||
for i in settings["filters"]:
|
|
||||||
if settings["filters"][i]["in_buffer"] == buffer_name:
|
|
||||||
regexp = settings["filters"][i]["regexp"]
|
|
||||||
word = settings["filters"][i]["word"]
|
|
||||||
# Added if/else for compatibility reasons.
|
|
||||||
if "allow_rts" in settings["filters"][i]:
|
|
||||||
allow_rts = settings["filters"][i]["allow_rts"]
|
|
||||||
else:
|
|
||||||
allow_rts = "True"
|
|
||||||
if "allow_quotes" in settings["filters"][i]:
|
|
||||||
allow_quotes = settings["filters"][i]["allow_quotes"]
|
|
||||||
else:
|
|
||||||
allow_quotes = "True"
|
|
||||||
if "allow_replies" in settings["filters"][i]:
|
|
||||||
allow_replies = settings["filters"][i]["allow_replies"]
|
|
||||||
else:
|
|
||||||
allow_replies = "True"
|
|
||||||
if allow_rts == "False" and "retweet" in tweet_data:
|
|
||||||
return False
|
|
||||||
if allow_quotes == "False" and "quote" in tweet_data:
|
|
||||||
return False
|
|
||||||
if allow_replies == "False" and "reply" in tweet_data:
|
|
||||||
return False
|
|
||||||
if word != "" and settings["filters"][i]["if_word_exists"]:
|
|
||||||
if word in getattr(tweet, value):
|
|
||||||
return False
|
|
||||||
elif word != "" and settings["filters"][i]["if_word_exists"] == False:
|
|
||||||
if word not in getattr(tweet, value):
|
|
||||||
return False
|
|
||||||
if settings["filters"][i]["in_lang"] == "True":
|
|
||||||
if getattr(tweet, "lang") not in settings["filters"][i]["languages"]:
|
|
||||||
return False
|
|
||||||
elif settings["filters"][i]["in_lang"] == "False":
|
|
||||||
if tweet.lang in settings["filters"][i]["languages"]:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def twitter_error(error):
|
|
||||||
if type(error) == Forbidden:
|
|
||||||
msg = _(u"Sorry, you are not authorised to see this status.")
|
|
||||||
elif type(error) == NotFound:
|
|
||||||
msg = _(u"No status found with that ID")
|
|
||||||
else:
|
|
||||||
msg = _(u"Error {0}").format(str(error),)
|
|
||||||
output.speak(msg)
|
|
||||||
|
|
||||||
def expand_urls(text, entities):
|
|
||||||
""" Expand all URLS present in text with information found in entities"""
|
|
||||||
if entities.get("urls") == None:
|
|
||||||
return text
|
|
||||||
urls = find_urls_in_text(text)
|
|
||||||
for url in entities["urls"]:
|
|
||||||
if url["url"] in text:
|
|
||||||
text = text.replace(url["url"], url["expanded_url"])
|
|
||||||
return text
|
|
||||||
|
|
||||||
def clean_mentions(text):
|
|
||||||
new_text = text
|
|
||||||
mentionned_people = [u for u in re.finditer("(?<=^|(?<=[^a-zA-Z0-9-\.]))@([A-Za-z0-9_]+)", text)]
|
|
||||||
if len(mentionned_people) <= 2:
|
|
||||||
return text
|
|
||||||
end = -2
|
|
||||||
total_users = 0
|
|
||||||
for user in mentionned_people:
|
|
||||||
if abs(user.start()-end) < 3:
|
|
||||||
new_text = new_text.replace(user.group(0), "", 1)
|
|
||||||
total_users = total_users+1
|
|
||||||
end = user.end()
|
|
||||||
if total_users-2 < 1:
|
|
||||||
return text
|
|
||||||
new_text = _("{user_1}, {user_2} and {all_users} more: {text}").format(user_1=mentionned_people[0].group(0), user_2=mentionned_people[1].group(0), all_users=total_users-2, text=new_text)
|
|
||||||
return new_text
|
|
@ -1,17 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class authorisationDialog(wx.Dialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(authorisationDialog, self).__init__(parent=None, title=_(u"Authorising account..."))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
static = wx.StaticText(panel, wx.NewId(), _(u"Enter your PIN code here"))
|
|
||||||
self.text = wx.TextCtrl(panel, -1)
|
|
||||||
self.ok = wx.Button(panel, wx.ID_OK)
|
|
||||||
self.cancel = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
sizer.Add(self.text, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.cancel, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
min = sizer.CalcMin()
|
|
||||||
self.SetClientSize(min)
|
|
@ -1,3 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import twitter, mastodon
|
from . import mastodon
|
||||||
from .panels import accountPanel, emptyPanel
|
from .panels import accountPanel, emptyPanel
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from .base import basePanel
|
|
||||||
from .dm import dmPanel
|
|
||||||
from .events import eventsPanel
|
|
||||||
from .favourites import favsPanel
|
|
||||||
from .lists import listPanel
|
|
||||||
from .people import peoplePanel
|
|
||||||
from .trends import trendsPanel
|
|
||||||
from .tweet_searches import searchPanel
|
|
||||||
from .user_searches import searchUsersPanel
|
|
@ -1,46 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class basePanel(wx.Panel):
|
|
||||||
|
|
||||||
def set_focus_function(self, f):
|
|
||||||
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, f)
|
|
||||||
|
|
||||||
def create_list(self):
|
|
||||||
self.list = widgets.list(self, _(u"User"), _(u"Text"), _(u"Date"), _(u"Client"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
|
|
||||||
self.list.set_windows_size(0, 60)
|
|
||||||
self.list.set_windows_size(1, 320)
|
|
||||||
self.list.set_windows_size(2, 110)
|
|
||||||
self.list.set_windows_size(3, 84)
|
|
||||||
self.list.set_size()
|
|
||||||
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(basePanel, self).__init__(parent)
|
|
||||||
self.name = name
|
|
||||||
self.type = "baseBuffer"
|
|
||||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.create_list()
|
|
||||||
self.tweet = wx.Button(self, -1, _(u"Tweet"))
|
|
||||||
self.retweet = wx.Button(self, -1, _(u"Retweet"))
|
|
||||||
self.reply = wx.Button(self, -1, _(u"Reply"))
|
|
||||||
self.dm = wx.Button(self, -1, _(u"Direct message"))
|
|
||||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnSizer.Add(self.tweet, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.retweet, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.reply, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.dm, 0, wx.ALL, 5)
|
|
||||||
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
||||||
self.sizer.Add(self.list.list, 0, wx.ALL|wx.EXPAND, 5)
|
|
||||||
self.SetSizer(self.sizer)
|
|
||||||
self.SetClientSize(self.sizer.CalcMin())
|
|
||||||
|
|
||||||
def set_position(self, reversed=False):
|
|
||||||
if reversed == False:
|
|
||||||
self.list.select_item(self.list.get_count()-1)
|
|
||||||
else:
|
|
||||||
self.list.select_item(0)
|
|
||||||
|
|
||||||
def set_focus_in_list(self):
|
|
||||||
self.list.list.SetFocus()
|
|
@ -1,13 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from .base import basePanel
|
|
||||||
|
|
||||||
class dmPanel(basePanel):
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
""" Class to DM'S. Reply and retweet buttons are not showed and they have your delete method for dm's."""
|
|
||||||
super(dmPanel, self).__init__(parent, name)
|
|
||||||
self.retweet.Disable()
|
|
||||||
self.reply.Disable()
|
|
||||||
self.type = "dm"
|
|
@ -1,25 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class eventsPanel(wx.Panel):
|
|
||||||
""" Buffer to show events. Different than tweets or people."""
|
|
||||||
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
self.type = "event"
|
|
||||||
super(eventsPanel, self).__init__(parent)
|
|
||||||
self.name = name
|
|
||||||
sizer = wx.BoxSizer()
|
|
||||||
self.list = widgets.list(self, _(u"Date"), _(u"Event"), size=(600,600), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
|
|
||||||
self.tweet = wx.Button(self, -1, _(u"Tweet"))
|
|
||||||
self.delete_event = wx.Button(self, -1, _(u"Remove event"))
|
|
||||||
|
|
||||||
def set_position(self, reversed=False):
|
|
||||||
if reversed == False:
|
|
||||||
self.list.select_item(self.list.get_count()-1)
|
|
||||||
else:
|
|
||||||
self.list.select_item(0)
|
|
||||||
|
|
||||||
def set_focus_in_list(self):
|
|
||||||
self.list.list.SetFocus()
|
|
@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from .base import basePanel
|
|
||||||
|
|
||||||
class favsPanel(basePanel):
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(favsPanel, self).__init__(parent, name)
|
|
||||||
self.type = "favourites_timeline"
|
|
@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from .base import basePanel
|
|
||||||
|
|
||||||
class listPanel(basePanel):
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(listPanel, self).__init__(parent, name)
|
|
||||||
self.type = "list"
|
|
@ -1,18 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
from .base import basePanel
|
|
||||||
|
|
||||||
class peoplePanel(basePanel):
|
|
||||||
""" Buffer used to show people."""
|
|
||||||
|
|
||||||
def create_list(self):
|
|
||||||
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
|
|
||||||
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(peoplePanel, self).__init__(parent, name)
|
|
||||||
self.type = "people"
|
|
||||||
self.reply.SetLabel(_(u"Mention"))
|
|
||||||
self.retweet.Disable()
|
|
@ -1,36 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class trendsPanel(wx.Panel):
|
|
||||||
def create_list(self):
|
|
||||||
""" Returns the list for put the tweets here."""
|
|
||||||
self.list = widgets.list(self, _(u"Trending topic"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
|
|
||||||
self.list.set_windows_size(0, 30)
|
|
||||||
self.list.set_size()
|
|
||||||
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(trendsPanel, self).__init__(parent)
|
|
||||||
self.type = "trends"
|
|
||||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.create_list()
|
|
||||||
self.tweet = wx.Button(self, -1, _(u"Tweet"))
|
|
||||||
self.tweetTrendBtn = wx.Button(self, -1, _(u"Tweet about this trend"))
|
|
||||||
self.search_topic = wx.Button(self, -1, _(u"Search topic"))
|
|
||||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnSizer.Add(self.tweet, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.tweetTrendBtn, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.search_topic, 0, wx.ALL, 5)
|
|
||||||
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
||||||
self.sizer.Add(self.list.list, 0, wx.ALL, 5)
|
|
||||||
self.SetSizer(self.sizer)
|
|
||||||
|
|
||||||
def set_position(self, reversed=False):
|
|
||||||
if reversed == False:
|
|
||||||
self.list.select_item(self.list.get_count()-1)
|
|
||||||
else:
|
|
||||||
self.list.select_item(0)
|
|
||||||
|
|
||||||
def set_focus_in_list(self):
|
|
||||||
self.list.list.SetFocus()
|
|
@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from .base import basePanel
|
|
||||||
|
|
||||||
class searchPanel(basePanel):
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(searchPanel, self).__init__(parent, name)
|
|
||||||
self.type = "search"
|
|
@ -1,16 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from .tweet_searches import searchPanel
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class searchUsersPanel(searchPanel):
|
|
||||||
def create_list(self):
|
|
||||||
""" Returns the list for put the tweets here."""
|
|
||||||
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
|
|
||||||
|
|
||||||
def __init__(self, parent, name):
|
|
||||||
super(searchUsersPanel, self).__init__(parent, name)
|
|
||||||
self.create_list()
|
|
||||||
self.type = "user_searches"
|
|
@ -1,17 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
import wx
|
||||||
import application
|
import application
|
||||||
|
|
||||||
def retweet_as_link(parent):
|
|
||||||
return wx.MessageDialog(parent, _(u"This retweet is over 140 characters. Would you like to post it as a mention to the poster with your comments and a link to the original tweet?"), application.name, wx.YES_NO|wx.ICON_QUESTION).ShowModal()
|
|
||||||
|
|
||||||
def retweet_question(parent):
|
|
||||||
return wx.MessageDialog(parent, _(u"Would you like to add a comment to this tweet?"), _("Retweet"), wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION).ShowModal()
|
|
||||||
|
|
||||||
def delete_tweet_dialog(parent):
|
|
||||||
return wx.MessageDialog(parent, _(u"Do you really want to delete this tweet? It will be deleted from Twitter as well."), _(u"Delete"), wx.ICON_QUESTION|wx.YES_NO).ShowModal()
|
|
||||||
|
|
||||||
def exit_dialog(parent):
|
def exit_dialog(parent):
|
||||||
dlg = wx.MessageDialog(parent, _(u"Do you really want to close {0}?").format(application.name,), _(u"Exit"), wx.YES_NO|wx.ICON_QUESTION)
|
dlg = wx.MessageDialog(parent, _(u"Do you really want to close {0}?").format(application.name,), _(u"Exit"), wx.YES_NO|wx.ICON_QUESTION)
|
||||||
return dlg.ShowModal()
|
return dlg.ShowModal()
|
||||||
@ -22,12 +12,6 @@ def needs_restart():
|
|||||||
def delete_user_from_db():
|
def delete_user_from_db():
|
||||||
return wx.MessageDialog(None, _(u"Are you sure you want to delete this user from the database? This user will not appear in autocomplete results anymore."), _(u"Confirm"), wx.YES_NO|wx.ICON_QUESTION).ShowModal()
|
return wx.MessageDialog(None, _(u"Are you sure you want to delete this user from the database? This user will not appear in autocomplete results anymore."), _(u"Confirm"), wx.YES_NO|wx.ICON_QUESTION).ShowModal()
|
||||||
|
|
||||||
def get_ignored_client():
|
|
||||||
entry = wx.TextEntryDialog(None, _(u"Enter the name of the client : "), _(u"Add client"))
|
|
||||||
if entry.ShowModal() == wx.ID_OK:
|
|
||||||
return entry.GetValue()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def clear_list():
|
def clear_list():
|
||||||
dlg = wx.MessageDialog(None, _(u"Do you really want to empty this buffer? It's items will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
|
dlg = wx.MessageDialog(None, _(u"Do you really want to empty this buffer? It's items will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
|
||||||
return dlg.ShowModal()
|
return dlg.ShowModal()
|
||||||
@ -41,56 +25,13 @@ def user_not_exist():
|
|||||||
def timeline_exist():
|
def timeline_exist():
|
||||||
return wx.MessageDialog(None, _(u"A timeline for this user already exists. You can't open another"), _(u"Existing timeline"), wx.ICON_ERROR).ShowModal()
|
return wx.MessageDialog(None, _(u"A timeline for this user already exists. You can't open another"), _(u"Existing timeline"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
def no_tweets():
|
|
||||||
return wx.MessageDialog(None, _(u"This user has no tweets, so you can't open a timeline for them."), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
def protected_user():
|
|
||||||
return wx.MessageDialog(None, _(u"This is a protected Twitter user, which means you can't open a timeline using the Streaming API. The user's tweets will not update due to a twitter policy. Do you want to continue?"), _(u"Warning"), wx.ICON_WARNING|wx.YES_NO).ShowModal()
|
|
||||||
|
|
||||||
def no_following():
|
|
||||||
return wx.MessageDialog(None, _(u"This is a protected user account, you need to follow this user to view their tweets or likes."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
def donation():
|
def donation():
|
||||||
dlg = wx.MessageDialog(None, _(u"If you like {0} we need your help to keep it going. Help us by donating to the project. This will help us pay for the server, the domain and some other things to ensure that {0} will be actively maintained. Your donation will give us the means to continue the development of {0}, and to keep {0} free. Would you like to donate now?").format(application.name), _(u"We need your help"), wx.ICON_QUESTION|wx.YES_NO)
|
dlg = wx.MessageDialog(None, _(u"If you like {0} we need your help to keep it going. Help us by donating to the project. This will help us pay for the server, the domain and some other things to ensure that {0} will be actively maintained. Your donation will give us the means to continue the development of {0}, and to keep {0} free. Would you like to donate now?").format(application.name), _(u"We need your help"), wx.ICON_QUESTION|wx.YES_NO)
|
||||||
return dlg.ShowModal()
|
return dlg.ShowModal()
|
||||||
|
|
||||||
def no_tweets():
|
|
||||||
return wx.MessageDialog(None, _(u"This user has no tweets. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
def no_favs():
|
|
||||||
return wx.MessageDialog(None, _(u"This user has no favorited tweets. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
def no_followers():
|
|
||||||
return wx.MessageDialog(None, _(u"This user has no followers. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
def no_friends():
|
|
||||||
return wx.MessageDialog(None, _(u"This user has no friends. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
def view_geodata(geotext):
|
|
||||||
"""Specific message dialog to display geolocation data"""
|
|
||||||
return wx.MessageDialog(None, _(u"Geolocation data: {0}").format(geotext), _(u"Geo data for this tweet")).ShowModal()
|
|
||||||
|
|
||||||
def changed_keymap():
|
def changed_keymap():
|
||||||
return wx.MessageDialog(None, _(u"TWBlue has detected that you're running windows 10 and has changed the default keymap to the Windows 10 keymap. It means that some keyboard shorcuts could be different. Please check the keystroke editor by pressing Alt+Win+K to see all available keystrokes for this keymap."), _(u"Information"), wx.OK).ShowModal()
|
return wx.MessageDialog(None, _(u"TWBlue has detected that you're running windows 10 and has changed the default keymap to the Windows 10 keymap. It means that some keyboard shorcuts could be different. Please check the keystroke editor by pressing Alt+Win+K to see all available keystrokes for this keymap."), _(u"Information"), wx.OK).ShowModal()
|
||||||
|
|
||||||
def unauthorized():
|
|
||||||
return wx.MessageDialog(None, _(u"You have been blocked from viewing this content"), _(u"Error"), wx.OK).ShowModal()
|
|
||||||
|
|
||||||
def blocked_timeline():
|
|
||||||
return wx.MessageDialog(None, _(u"You have been blocked from viewing someone's content. In order to avoid conflicts with the full session, TWBlue will remove the affected timeline."), _(u"Error"), wx.OK).ShowModal()
|
|
||||||
|
|
||||||
def suspended_user():
|
|
||||||
return wx.MessageDialog(None, _(u"TWBlue cannot load this timeline because the user has been suspended from Twitter."), _(u"Error"), wx.OK).ShowModal()
|
|
||||||
|
|
||||||
def delete_filter():
|
|
||||||
return wx.MessageDialog(None, _(u"Do you really want to delete this filter?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO).ShowModal()
|
|
||||||
|
|
||||||
def existing_filter():
|
|
||||||
return wx.MessageDialog(None, _(u"This filter already exists. Please use a different title"), _(u"Error"), wx.OK).ShowModal()
|
|
||||||
|
|
||||||
def common_error(reason):
|
|
||||||
return wx.MessageDialog(None, reason, _(u"Error"), wx.OK).ShowModal()
|
|
||||||
|
|
||||||
def invalid_configuration():
|
def invalid_configuration():
|
||||||
return wx.MessageDialog(None, _("The configuration file is invalid."), _("Error"), wx.ICON_ERROR).ShowModal()
|
return wx.MessageDialog(None, _("The configuration file is invalid."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from . import baseDialog, trends, configuration, lists, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs, userAliasDialogs
|
|
@ -95,45 +95,6 @@ class proxy(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
sizer.Add(serverBox, 0, wx.ALL, 5)
|
sizer.Add(serverBox, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self, parent):
|
|
||||||
super(generalAccount, self).__init__(parent)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
userAutocompletionBox = wx.StaticBox(self, label=_("User autocompletion settings"))
|
|
||||||
self.userAutocompletionScan = wx.Button(self, wx.ID_ANY, _("Scan account and add friends and followers to the user autocompletion database"))
|
|
||||||
self.userAutocompletionManage = wx.Button(self, wx.ID_ANY, _("Manage autocompletion database"))
|
|
||||||
autocompletionSizer = wx.StaticBoxSizer(userAutocompletionBox, wx.HORIZONTAL)
|
|
||||||
autocompletionSizer.Add(self.userAutocompletionScan, 0, wx.ALL, 5)
|
|
||||||
autocompletionSizer.Add(self.userAutocompletionManage, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(autocompletionSizer, 0, wx.ALL, 5)
|
|
||||||
self.relative_time = wx.CheckBox(self, wx.ID_ANY, _(U"Relative timestamps"))
|
|
||||||
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
|
||||||
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
tweetsPerCallBox.Add(wx.StaticText(self, -1, _(u"Items on each API call")), 0, wx.ALL, 5)
|
|
||||||
self.itemsPerApiCall = wx.SpinCtrl(self, wx.ID_ANY)
|
|
||||||
self.itemsPerApiCall.SetRange(0, 200)
|
|
||||||
self.itemsPerApiCall.SetSize(self.itemsPerApiCall.GetBestSize())
|
|
||||||
tweetsPerCallBox.Add(self.itemsPerApiCall, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(tweetsPerCallBox, 0, wx.ALL, 5)
|
|
||||||
self.reverse_timelines = wx.CheckBox(self, wx.ID_ANY, _(u"Inverted buffers: The newest tweets will be shown at the beginning while the oldest at the end"))
|
|
||||||
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
|
|
||||||
lbl = wx.StaticText(self, wx.ID_ANY, _(u"Retweet mode"))
|
|
||||||
self.retweet_mode = wx.ComboBox(self, wx.ID_ANY, choices=[_(u"Ask"), _(u"Retweet without comments"), _(u"Retweet with comments")], style=wx.CB_READONLY)
|
|
||||||
rMode = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
rMode.Add(lbl, 0, wx.ALL, 5)
|
|
||||||
rMode.Add(self.retweet_mode, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(rMode, 0, wx.ALL, 5)
|
|
||||||
self.show_screen_names = wx.CheckBox(self, wx.ID_ANY, _(U"Show screen names instead of full names"))
|
|
||||||
sizer.Add(self.show_screen_names, 0, wx.ALL, 5)
|
|
||||||
self.hide_emojis = wx.CheckBox(self, wx.ID_ANY, _("hide emojis in usernames"))
|
|
||||||
sizer.Add(self.hide_emojis, 0, wx.ALL, 5)
|
|
||||||
PersistSizeLabel = wx.StaticText(self, -1, _(u"Number of items per buffer to cache in database (0 to disable caching, blank for unlimited)"))
|
|
||||||
self.persist_size = wx.TextCtrl(self, -1)
|
|
||||||
sizer.Add(PersistSizeLabel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.persist_size, 0, wx.ALL, 5)
|
|
||||||
self.load_cache_in_memory = wx.CheckBox(self, wx.NewId(), _("Load cache for tweets in memory (much faster in big datasets but requires more RAM)"))
|
|
||||||
self.SetSizer(sizer)
|
|
||||||
|
|
||||||
class reporting(wx.Panel, baseDialog.BaseWXDialog):
|
class reporting(wx.Panel, baseDialog.BaseWXDialog):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(reporting, self).__init__(parent)
|
super(reporting, self).__init__(parent)
|
||||||
@ -240,130 +201,6 @@ class other_buffers(wx.Panel):
|
|||||||
buffers_list.append(self.buffers.get_text_column(i, 0))
|
buffers_list.append(self.buffers.get_text_column(i, 0))
|
||||||
return buffers_list
|
return buffers_list
|
||||||
|
|
||||||
class templates(wx.Panel, baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self, parent, tweet_template, dm_template, sent_dm_template, person_template):
|
|
||||||
super(templates, self).__init__(parent)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.tweet = wx.Button(self, wx.ID_ANY, _("Edit template for tweets. Current template: {}").format(tweet_template))
|
|
||||||
sizer.Add(self.tweet, 0, wx.ALL, 5)
|
|
||||||
self.dm = wx.Button(self, wx.ID_ANY, _("Edit template for direct messages. Current template: {}").format(dm_template))
|
|
||||||
sizer.Add(self.dm, 0, wx.ALL, 5)
|
|
||||||
self.sent_dm = wx.Button(self, wx.ID_ANY, _("Edit template for sent direct messages. Current template: {}").format(sent_dm_template))
|
|
||||||
sizer.Add(self.sent_dm, 0, wx.ALL, 5)
|
|
||||||
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for persons. Current template: {}").format(person_template))
|
|
||||||
sizer.Add(self.person, 0, wx.ALL, 5)
|
|
||||||
self.SetSizer(sizer)
|
|
||||||
|
|
||||||
class ignoredClients(wx.Panel):
|
|
||||||
def __init__(self, parent, choices):
|
|
||||||
super(ignoredClients, self).__init__(parent=parent)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
label = wx.StaticText(self, -1, _(u"Ignored clients"))
|
|
||||||
self.clients = wx.ListBox(self, -1, choices=choices)
|
|
||||||
self.clients.SetSize(self.clients.GetBestSize())
|
|
||||||
clientsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
clientsBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
clientsBox.Add(self.clients, 0, wx.ALL, 5)
|
|
||||||
self.add = wx.Button(self, -1, _(u"Add client"))
|
|
||||||
self.remove = wx.Button(self, -1, _(u"Remove client"))
|
|
||||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnBox.Add(self.add, 0, wx.ALL, 5)
|
|
||||||
btnBox.Add(self.remove, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(clientsBox, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
|
||||||
self.SetSizer(sizer)
|
|
||||||
|
|
||||||
def append(self, client):
|
|
||||||
self.clients.Append(client)
|
|
||||||
|
|
||||||
def get_clients(self):
|
|
||||||
return self.clients.GetCount()
|
|
||||||
|
|
||||||
def get_client_id(self):
|
|
||||||
return self.clients.GetSelection()
|
|
||||||
|
|
||||||
def remove_(self, id):
|
|
||||||
self.clients.Delete(id)
|
|
||||||
|
|
||||||
class sound(wx.Panel):
|
|
||||||
def __init__(self, parent, input_devices, output_devices, soundpacks):
|
|
||||||
wx.Panel.__init__(self, parent)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
volume = wx.StaticText(self, -1, _(u"Volume"))
|
|
||||||
self.volumeCtrl = wx.Slider(self)
|
|
||||||
# Connect a key handler here to handle volume slider being inverted when moving with up and down arrows.
|
|
||||||
# see https://github.com/manuelcortez/TWBlue/issues/261
|
|
||||||
widgetUtils.connect_event(self.volumeCtrl, widgetUtils.KEYPRESS, self.on_keypress)
|
|
||||||
self.volumeCtrl.SetRange(0, 100)
|
|
||||||
self.volumeCtrl.SetSize(self.volumeCtrl.GetBestSize())
|
|
||||||
volumeBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
volumeBox.Add(volume, 0, wx.ALL, 5)
|
|
||||||
volumeBox.Add(self.volumeCtrl, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(volumeBox, 0, wx.ALL, 5)
|
|
||||||
self.session_mute = wx.CheckBox(self, -1, _(u"Session mute"))
|
|
||||||
sizer.Add(self.session_mute, 0, wx.ALL, 5)
|
|
||||||
output_label = wx.StaticText(self, -1, _(u"Output device"))
|
|
||||||
self.output = wx.ComboBox(self, -1, choices=output_devices, style=wx.CB_READONLY)
|
|
||||||
self.output.SetSize(self.output.GetBestSize())
|
|
||||||
outputBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
outputBox.Add(output_label, 0, wx.ALL, 5)
|
|
||||||
outputBox.Add(self.output, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(outputBox, 0, wx.ALL, 5)
|
|
||||||
input_label = wx.StaticText(self, -1, _(u"Input device"))
|
|
||||||
self.input = wx.ComboBox(self, -1, choices=input_devices, style=wx.CB_READONLY)
|
|
||||||
self.input.SetSize(self.input.GetBestSize())
|
|
||||||
inputBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
inputBox.Add(input_label, 0, wx.ALL, 5)
|
|
||||||
inputBox.Add(self.input, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(inputBox, 0, wx.ALL, 5)
|
|
||||||
soundBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
soundpack_label = wx.StaticText(self, -1, _(u"Sound pack"))
|
|
||||||
self.soundpack = wx.ComboBox(self, -1, choices=soundpacks, style=wx.CB_READONLY)
|
|
||||||
self.soundpack.SetSize(self.soundpack.GetBestSize())
|
|
||||||
soundBox.Add(soundpack_label, 0, wx.ALL, 5)
|
|
||||||
soundBox.Add(self.soundpack, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(soundBox, 0, wx.ALL, 5)
|
|
||||||
self.indicate_audio = wx.CheckBox(self, -1, _(u"Indicate audio tweets with sound"))
|
|
||||||
sizer.Add(self.indicate_audio, 0, wx.ALL, 5)
|
|
||||||
self.indicate_geo = wx.CheckBox(self, -1, _(u"Indicate geotweets with sound"))
|
|
||||||
sizer.Add(self.indicate_geo, 0, wx.ALL, 5)
|
|
||||||
self.indicate_img = wx.CheckBox(self, -1, _(u"Indicate tweets containing images with sound"))
|
|
||||||
sizer.Add(self.indicate_img, 0, wx.ALL, 5)
|
|
||||||
self.SetSizer(sizer)
|
|
||||||
|
|
||||||
def on_keypress(self, event, *args, **kwargs):
|
|
||||||
""" Invert movement of up and down arrow keys when dealing with a wX Slider.
|
|
||||||
See https://github.com/manuelcortez/TWBlue/issues/261
|
|
||||||
and http://trac.wxwidgets.org/ticket/2068
|
|
||||||
"""
|
|
||||||
keycode = event.GetKeyCode()
|
|
||||||
if keycode == wx.WXK_UP:
|
|
||||||
return self.volumeCtrl.SetValue(self.volumeCtrl.GetValue()+1)
|
|
||||||
elif keycode == wx.WXK_DOWN:
|
|
||||||
return self.volumeCtrl.SetValue(self.volumeCtrl.GetValue()-1)
|
|
||||||
event.Skip()
|
|
||||||
|
|
||||||
def get(self, control):
|
|
||||||
return getattr(self, control).GetStringSelection()
|
|
||||||
|
|
||||||
class extrasPanel(wx.Panel):
|
|
||||||
def __init__(self, parent, ocr_languages=[], translation_languages=[]):
|
|
||||||
super(extrasPanel, self).__init__(parent)
|
|
||||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
OCRBox = wx.StaticBox(self, label=_(u"Language for OCR"))
|
|
||||||
self.ocr_lang = wx.ListBox(self, -1, choices=ocr_languages)
|
|
||||||
self.ocr_lang.SetSize(self.ocr_lang.GetBestSize())
|
|
||||||
ocrLanguageSizer = wx.StaticBoxSizer(OCRBox, wx.HORIZONTAL)
|
|
||||||
ocrLanguageSizer.Add(self.ocr_lang, 0, wx.ALL, 5)
|
|
||||||
mainSizer.Add(ocrLanguageSizer, 0, wx.ALL, 5)
|
|
||||||
lbl = wx.StaticText(self, wx.ID_ANY, _(u"API Key for SndUp"))
|
|
||||||
self.sndup_apiKey = wx.TextCtrl(self, -1)
|
|
||||||
sndupBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sndupBox.Add(lbl, 0, wx.ALL, 5)
|
|
||||||
sndupBox.Add(self.sndup_apiKey, 0, wx.ALL, 5)
|
|
||||||
mainSizer.Add(sndupBox, 0, wx.ALL, 5)
|
|
||||||
self.SetSizer(mainSizer)
|
|
||||||
|
|
||||||
class configurationDialog(baseDialog.BaseWXDialog):
|
class configurationDialog(baseDialog.BaseWXDialog):
|
||||||
def set_title(self, title):
|
def set_title(self, title):
|
||||||
self.SetTitle(title)
|
self.SetTitle(title)
|
||||||
@ -384,35 +221,6 @@ class configurationDialog(baseDialog.BaseWXDialog):
|
|||||||
self.proxy = proxy(self.notebook, proxyTypes)
|
self.proxy = proxy(self.notebook, proxyTypes)
|
||||||
self.notebook.AddPage(self.proxy, _(u"Proxy"))
|
self.notebook.AddPage(self.proxy, _(u"Proxy"))
|
||||||
|
|
||||||
def create_general_account(self):
|
|
||||||
self.general = generalAccount(self.notebook)
|
|
||||||
self.notebook.AddPage(self.general, _(u"General"))
|
|
||||||
self.general.SetFocus()
|
|
||||||
|
|
||||||
def create_reporting(self):
|
|
||||||
self.reporting = reporting(self.notebook)
|
|
||||||
self.notebook.AddPage(self.reporting, _(u"Feedback"))
|
|
||||||
|
|
||||||
def create_other_buffers(self):
|
|
||||||
self.buffers = other_buffers(self.notebook)
|
|
||||||
self.notebook.AddPage(self.buffers, _(u"Buffers"))
|
|
||||||
|
|
||||||
def create_ignored_clients(self, ignored_clients_list):
|
|
||||||
self.ignored_clients = ignoredClients(self.notebook, ignored_clients_list)
|
|
||||||
self.notebook.AddPage(self.ignored_clients, _(u"Ignored clients"))
|
|
||||||
|
|
||||||
def create_templates(self, tweet_template, dm_template, sent_dm_template, person_template):
|
|
||||||
self.templates = templates(self.notebook, tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template)
|
|
||||||
self.notebook.AddPage(self.templates, _("Templates"))
|
|
||||||
|
|
||||||
def create_sound(self, output_devices, input_devices, soundpacks):
|
|
||||||
self.sound = sound(self.notebook, output_devices, input_devices, soundpacks)
|
|
||||||
self.notebook.AddPage(self.sound, _(u"Sound"))
|
|
||||||
|
|
||||||
def create_extras(self, ocr_languages=[], translator_languages=[]):
|
|
||||||
self.extras = extrasPanel(self.notebook, ocr_languages, translator_languages)
|
|
||||||
self.notebook.AddPage(self.extras, _(u"Extras"))
|
|
||||||
|
|
||||||
def realize(self):
|
def realize(self):
|
||||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
from . import baseDialog
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class filterDialog(baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self, value="", languages=[]):
|
|
||||||
super(filterDialog, self).__init__(None, -1)
|
|
||||||
self.langs_list = languages
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.SetTitle(_(u"Create a filter for this buffer"))
|
|
||||||
label = wx.StaticText(panel, wx.ID_ANY, _(u"Filter title"))
|
|
||||||
self.title = wx.TextCtrl(panel, -1, value)
|
|
||||||
dc = wx.WindowDC(self.title)
|
|
||||||
dc.SetFont(self.title.GetFont())
|
|
||||||
self.title.SetSize(dc.GetTextExtent("0"*40))
|
|
||||||
self.title.SetFocus()
|
|
||||||
tsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
tsizer.Add(label, 0, wx.ALL, 5)
|
|
||||||
tsizer.Add(self.title, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(tsizer, 0, wx.ALL, 5)
|
|
||||||
staticbox = wx.StaticBox(panel, label=_(u"Filter by word"))
|
|
||||||
self.contains = wx.RadioButton(panel, -1, _(u"Ignore tweets wich contain the following word"), style=wx.RB_GROUP)
|
|
||||||
self.doesnt_contain = wx.RadioButton(panel, -1, _(u"Ignore tweets without the following word"))
|
|
||||||
radioSizer1 = wx.StaticBoxSizer(staticbox, wx.HORIZONTAL)
|
|
||||||
radioSizer1.Add(self.contains, 0, wx.ALL, 5)
|
|
||||||
radioSizer1.Add(self.doesnt_contain, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(radioSizer1, 0, wx.ALL, 5)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"word"))
|
|
||||||
self.term = wx.TextCtrl(panel, -1, value)
|
|
||||||
dc = wx.WindowDC(self.term)
|
|
||||||
dc.SetFont(self.term.GetFont())
|
|
||||||
self.term.SetSize(dc.GetTextExtent("0"*40))
|
|
||||||
self.allow_rts = wx.CheckBox(panel, wx.ID_ANY, _(u"Allow retweets"))
|
|
||||||
self.allow_quotes = wx.CheckBox(panel, wx.ID_ANY, _(u"Allow quoted tweets"))
|
|
||||||
self.allow_replies = wx.CheckBox(panel, wx.ID_ANY, _(u"Allow replies"))
|
|
||||||
self.allow_rts.SetValue(True)
|
|
||||||
self.allow_quotes.SetValue(True)
|
|
||||||
self.allow_replies.SetValue(True)
|
|
||||||
bsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
bsizer.Add(label, 0, wx.ALL, 5)
|
|
||||||
bsizer.Add(self.term, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(bsizer, 0, wx.ALL, 5)
|
|
||||||
self.regexp = wx.CheckBox(panel, wx.ID_ANY, _(u"Use this term as a regular expression"))
|
|
||||||
sizer.Add(self.regexp, 0, wx.ALL, 5)
|
|
||||||
staticbox = wx.StaticBox(panel, label=_(u"Filter by language"))
|
|
||||||
self.load_language = wx.RadioButton(panel, -1, _(u"Load tweets in the following languages"), style=wx.RB_GROUP)
|
|
||||||
self.ignore_language = wx.RadioButton(panel, -1, _(u"Ignore tweets in the following languages"))
|
|
||||||
self.skip_language_filtering = wx.RadioButton(panel, -1, _(u"Don't filter by language"))
|
|
||||||
self.skip_language_filtering.SetValue(True)
|
|
||||||
widgetUtils.connect_event(self.load_language, widgetUtils.RADIOBUTTON, self.show_language_options)
|
|
||||||
widgetUtils.connect_event(self.ignore_language, widgetUtils.RADIOBUTTON, self.show_language_options)
|
|
||||||
widgetUtils.connect_event(self.skip_language_filtering, widgetUtils.RADIOBUTTON, self.hide_language_options)
|
|
||||||
radioSizer2 = wx.StaticBoxSizer(staticbox, wx.HORIZONTAL)
|
|
||||||
radioSizer2.Add(self.load_language, 0, wx.ALL, 5)
|
|
||||||
radioSizer2.Add(self.ignore_language, 0, wx.ALL, 5)
|
|
||||||
radioSizer2.Add(self.skip_language_filtering, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(radioSizer2, 0, wx.ALL, 5)
|
|
||||||
self.indexes = []
|
|
||||||
langsLabel = wx.StaticText(panel, -1, _(u"Supported languages"))
|
|
||||||
self.cb = wx.ComboBox(panel, -1, choices=languages, value=languages[0])
|
|
||||||
langsSizer = wx.BoxSizer()
|
|
||||||
langsSizer.Add(langsLabel, 0, wx.ALL, 5)
|
|
||||||
langsSizer.Add(self.cb, 0, wx.ALL, 5)
|
|
||||||
self.add = wx.Button(panel, wx.ID_ANY, _(u"Add selected language to filter"))
|
|
||||||
self.add.Bind(wx.EVT_BUTTON, self.add_lang)
|
|
||||||
langsSizer.Add(self.add, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(langsSizer, 0, wx.ALL, 5)
|
|
||||||
lbl = wx.StaticText(panel, wx.ID_ANY, _(u"Selected languages"))
|
|
||||||
self.langs = wx.ListBox(panel, -1)
|
|
||||||
self.remove = wx.Button(panel, wx.ID_ANY, _(u"Remove"))
|
|
||||||
self.remove.Bind(wx.EVT_BUTTON, self.remove_lang)
|
|
||||||
selectionSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
selectionSizer.Add(lbl, 0, wx.ALL, 5)
|
|
||||||
selectionSizer.Add(self.langs, 0, wx.ALL, 5)
|
|
||||||
selectionSizer.Add(self.remove, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(selectionSizer, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
|
||||||
ok.SetDefault()
|
|
||||||
ok.Bind(wx.EVT_BUTTON, self.validate_title)
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
|
||||||
btnsizer = wx.BoxSizer()
|
|
||||||
btnsizer.Add(ok, 0, wx.ALL, 5)
|
|
||||||
btnsizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.hide_language_options()
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def get_lang(self):
|
|
||||||
return self.cb.GetValue()
|
|
||||||
|
|
||||||
def add_lang(self, *args, **kwargs):
|
|
||||||
selection = self.get_lang()
|
|
||||||
if selection in self.langs_list:
|
|
||||||
self.langs.Append(selection)
|
|
||||||
self.indexes.append(selection)
|
|
||||||
|
|
||||||
def remove_lang(self, *args, **kwargs):
|
|
||||||
n = self.langs.GetSelection()
|
|
||||||
v = self.langs.GetStringSelection()
|
|
||||||
self.langs.Delete(n)
|
|
||||||
self.indexes.remove(v)
|
|
||||||
|
|
||||||
def get_selected_langs(self):
|
|
||||||
return self.indexes
|
|
||||||
|
|
||||||
def hide_language_options(self, *args, **kwargs):
|
|
||||||
for i in [self.cb, self.add, self.langs, self.remove]:
|
|
||||||
i.Hide()
|
|
||||||
|
|
||||||
def show_language_options(self, *args, **kwargs):
|
|
||||||
for i in [self.cb, self.add, self.langs, self.remove]:
|
|
||||||
i.Show()
|
|
||||||
|
|
||||||
def validate_title(self, *args, **kwargs):
|
|
||||||
if self.title.GetValue() == "" or self.title.GetValue() == None:
|
|
||||||
return wx.MessageDialog(self, _("You must define a name for the filter before creating it."), _("Missing filter name"), wx.ICON_ERROR).ShowModal()
|
|
||||||
self.EndModal(wx.ID_OK)
|
|
||||||
|
|
||||||
class filterManagerDialog(widgetUtils.BaseDialog):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(filterManagerDialog, self).__init__(parent=None, *args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Manage filters"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Filters"))
|
|
||||||
self.filters = widgets.list(panel, _(u"Filter"), _(u"Buffer"), _(u"Filter by word"), _(u"Filter by language"), size=(800, 800), style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
|
|
||||||
self.filters.list.SetFocus()
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
sizer.Add(label)
|
|
||||||
sizer.Add(self.filters.list)
|
|
||||||
self.edit = wx.Button(panel, wx.ID_ANY, _(u"Edit"))
|
|
||||||
self.edit.Enable(False)
|
|
||||||
self.delete = wx.Button(panel, wx.ID_ANY, _(u"Remove"))
|
|
||||||
self.cancel = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
btnSizer = wx.BoxSizer()
|
|
||||||
btnSizer.Add(self.edit, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.delete, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(self.cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
|
|
||||||
def get_item(self):
|
|
||||||
return self.filters.get_selected()
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self.filters.clear()
|
|
@ -1,150 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class listViewer(widgetUtils.BaseDialog):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(listViewer, self).__init__(parent=None, *args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Lists manager"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Lists"))
|
|
||||||
self.lista = widgets.list(panel, _(u"List"), _(u"Description"), _(u"Owner"), _(u"Members"), _(u"mode"), size=(800, 800), style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
|
|
||||||
self.lista.list.SetFocus()
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
sizer.Add(label)
|
|
||||||
sizer.Add(self.lista.list)
|
|
||||||
self.createBtn = wx.Button(panel, wx.ID_ANY, _(u"Create a new list"))
|
|
||||||
self.editBtn = wx.Button(panel, -1, _(u"Edit"))
|
|
||||||
self.deleteBtn = wx.Button(panel, -1, _(u"Remove"))
|
|
||||||
self.view = wx.Button(panel, -1, _(u"Open in buffer"))
|
|
||||||
# self.members = wx.Button(panel, -1, _(u"View members"))
|
|
||||||
# self.members.Disable()
|
|
||||||
# self.subscriptors = wx.Button(panel, -1, _(u"View subscribers"))
|
|
||||||
# self.subscriptors.Disable()
|
|
||||||
# self.get_linkBtn = wx.Button(panel, -1, _(u"Get link for the list"))
|
|
||||||
# self.get_linkBtn.Bind(wx.EVT_BUTTON, self.onGetLink)
|
|
||||||
self.cancelBtn = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
btnSizer = wx.BoxSizer()
|
|
||||||
btnSizer.Add(self.createBtn)
|
|
||||||
btnSizer.Add(self.editBtn)
|
|
||||||
btnSizer.Add(self.cancelBtn)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
|
|
||||||
def populate_list(self, lists, clear=False):
|
|
||||||
if clear == True:
|
|
||||||
self.clear()
|
|
||||||
for item in lists:
|
|
||||||
self.lista.insert_item(False, *item)
|
|
||||||
|
|
||||||
def get_item(self):
|
|
||||||
return self.lista.get_selected()
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self.lista.clear()
|
|
||||||
|
|
||||||
class userListViewer(listViewer):
|
|
||||||
def __init__(self, username, *args, **kwargs):
|
|
||||||
self.username = username
|
|
||||||
super(userListViewer, self).__init__(*args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Viewing lists for %s") % (self.username))
|
|
||||||
self.createBtn.SetLabel(_(u"Subscribe"))
|
|
||||||
self.deleteBtn.SetLabel(_(u"Unsubscribe"))
|
|
||||||
self.editBtn.Disable()
|
|
||||||
self.view.Disable()
|
|
||||||
|
|
||||||
class createListDialog(widgetUtils.BaseDialog):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(createListDialog, self).__init__(parent=None, *args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Create a new list"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
name = wx.StaticText(panel, -1, _(u"Name (20 characters maximun)"))
|
|
||||||
self.name = wx.TextCtrl(panel, -1)
|
|
||||||
nameSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
nameSizer.Add(name)
|
|
||||||
nameSizer.Add(self.name)
|
|
||||||
description = wx.StaticText(panel, -1, _(u"Description"))
|
|
||||||
self.description = wx.TextCtrl(panel, -1)
|
|
||||||
descriptionSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
descriptionSizer.Add(description)
|
|
||||||
descriptionSizer.Add(self.description)
|
|
||||||
mode = wx.StaticText(panel, -1, _(u"Mode"))
|
|
||||||
self.public = wx.RadioButton(panel, -1, _(u"Public"), style=wx.RB_GROUP)
|
|
||||||
self.private = wx.RadioButton(panel, -1, _(u"Private"))
|
|
||||||
modeBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
modeBox.Add(mode)
|
|
||||||
modeBox.Add(self.public)
|
|
||||||
modeBox.Add(self.private)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK)
|
|
||||||
ok.SetDefault()
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnBox.Add(ok)
|
|
||||||
btnBox.Add(cancel)
|
|
||||||
sizer.Add(nameSizer)
|
|
||||||
sizer.Add(descriptionSizer)
|
|
||||||
sizer.Add(modeBox)
|
|
||||||
sizer.Add(btnBox)
|
|
||||||
|
|
||||||
class editListDialog(createListDialog):
|
|
||||||
|
|
||||||
def __init__(self, list, *args, **kwargs):
|
|
||||||
super(editListDialog, self).__init__(*args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Editing the list %s") % (list.name))
|
|
||||||
self.name.ChangeValue(list.name)
|
|
||||||
self.description.ChangeValue(list.description)
|
|
||||||
if list.mode == "public":
|
|
||||||
self.public.SetValue(True)
|
|
||||||
else:
|
|
||||||
self.private.SetValue(True)
|
|
||||||
|
|
||||||
class addUserListDialog(listViewer):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(addUserListDialog, self).__init__(*args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Select a list to add the user"))
|
|
||||||
self.createBtn.SetLabel(_(u"Add"))
|
|
||||||
self.createBtn.SetDefault()
|
|
||||||
self.createBtn.Bind(wx.EVT_BUTTON, self.ok)
|
|
||||||
self.editBtn.Disable()
|
|
||||||
self.view.Disable()
|
|
||||||
# self.subscriptors.Disable()
|
|
||||||
# self.members.Disable()
|
|
||||||
self.deleteBtn.Disable()
|
|
||||||
widgetUtils.connect_event(self.lista.list, widgetUtils.KEYPRESS, self.on_keypress)
|
|
||||||
|
|
||||||
def on_keypress(self, event):
|
|
||||||
"""Catch return and execute ok()"""
|
|
||||||
if event.GetKeyCode() == wx.WXK_RETURN:
|
|
||||||
return self.ok()
|
|
||||||
event.Skip()
|
|
||||||
|
|
||||||
def ok(self, *args, **kwargs):
|
|
||||||
self.EndModal(wx.ID_OK)
|
|
||||||
|
|
||||||
class removeUserListDialog(listViewer):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(removeUserListDialog, self).__init__(*args, **kwargs)
|
|
||||||
self.SetTitle(_(u"Select a list to remove the user"))
|
|
||||||
self.createBtn.SetLabel(_(u"Remove"))
|
|
||||||
self.createBtn.SetDefault()
|
|
||||||
self.createBtn.SetId(wx.ID_OK)
|
|
||||||
self.editBtn.Disable()
|
|
||||||
self.view.Disable()
|
|
||||||
# self.subscriptors.Disable()
|
|
||||||
# self.members.Disable()
|
|
||||||
self.deleteBtn.Disable()
|
|
||||||
widgetUtils.connect_event(self.lista.list, widgetUtils.KEYPRESS, self.on_keypress)
|
|
||||||
|
|
||||||
def on_keypress(self, event):
|
|
||||||
"""Catch return and execute EndModal()"""
|
|
||||||
if event.GetKeyCode() == wx.WXK_RETURN:
|
|
||||||
return self.EndModal(wx.ID_OK)
|
|
||||||
event.Skip()
|
|
||||||
|
|
||||||
def remove_list():
|
|
||||||
return wx.MessageDialog(None, _("Do you really want to delete this list?"), _("Delete"), wx.YES_NO).ShowModal()
|
|
||||||
|
|
7
src/wxUI/dialogs/mastodon/convert to 480p.txt
Normal file
7
src/wxUI/dialogs/mastodon/convert to 480p.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
ffmpeg -hwaccel qsv -c:v h264_qsv -i "$1" -map 0 -c copy -c:v hevc_qsv -preset slow -global_quality 22 -look_ahead 1 "final/${1%.*}.mkv"
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
for i in *.wmv;
|
||||||
|
do
|
||||||
|
ffmpeg -i "$i" -c:v libx264 -c:a aac -vf scale=640:480 -crf 20 "final/$i.mp4"
|
||||||
|
done
|
@ -1,73 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import widgetUtils
|
|
||||||
from . import baseDialog
|
|
||||||
import wx
|
|
||||||
from extra import translator
|
|
||||||
|
|
||||||
class searchDialog(baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self, value=""):
|
|
||||||
super(searchDialog, self).__init__(None, -1)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.SetTitle(_(u"Search on Twitter"))
|
|
||||||
label = wx.StaticText(panel, -1, _(u"&Search"))
|
|
||||||
self.term = wx.TextCtrl(panel, -1, value)
|
|
||||||
self.term.SetFocus()
|
|
||||||
dc = wx.WindowDC(self.term)
|
|
||||||
dc.SetFont(self.term.GetFont())
|
|
||||||
self.term.SetSize(dc.GetTextExtent("0"*40))
|
|
||||||
sizer.Add(label, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.term, 0, wx.ALL, 5)
|
|
||||||
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
|
|
||||||
self.users = wx.RadioButton(panel, -1, _(u"Users"))
|
|
||||||
widgetUtils.connect_event(self.tweets, widgetUtils.RADIOBUTTON, self.show_advanced_search)
|
|
||||||
widgetUtils.connect_event(self.users, widgetUtils.RADIOBUTTON, self.hide_advanced_search)
|
|
||||||
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
radioSizer.Add(self.tweets, 0, wx.ALL, 5)
|
|
||||||
radioSizer.Add(self.users, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
|
||||||
lang = wx.StaticText(panel, -1, _(u"&Language for results: "))
|
|
||||||
langs = [x for x in list(translator.translator.languages.values())]
|
|
||||||
langs.insert(0, _(u"any"))
|
|
||||||
self.lang = wx.ComboBox(panel, -1, choices=langs, value=langs[0], style = wx.CB_READONLY)
|
|
||||||
langBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
langBox.Add(lang, 0, wx.ALL, 5)
|
|
||||||
langBox.Add(self.lang, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(langBox, 0, wx.ALL, 5)
|
|
||||||
resulttype = wx.StaticText(panel, -1, _(U"Results &type: "))
|
|
||||||
self.resultstype = wx.ComboBox(panel, -1, choices=[_(u"Mixed"), _(u"Recent"), _(u"Popular")], value=_(u"Mixed"), style=wx.CB_READONLY)
|
|
||||||
rBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
rBox.Add(resulttype, 0, wx.ALL, 5)
|
|
||||||
rBox.Add(self.resultstype, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(rBox, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"&OK"))
|
|
||||||
ok.SetDefault()
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Close"))
|
|
||||||
btnsizer = wx.BoxSizer()
|
|
||||||
btnsizer.Add(ok, 0, wx.ALL, 5)
|
|
||||||
btnsizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def get_language(self):
|
|
||||||
l = self.lang.GetStringSelection()
|
|
||||||
if l == _(u"any"):
|
|
||||||
return ""
|
|
||||||
for langcode, langname in translator.translator.languages.items():
|
|
||||||
if langname == l:
|
|
||||||
return langcode
|
|
||||||
|
|
||||||
def get_result_type(self):
|
|
||||||
r = self.resultstype.GetValue()
|
|
||||||
if r == _(u"Mixed"): return "mixed"
|
|
||||||
elif r == _(u"Recent"): return "recent"
|
|
||||||
elif r == _(u"Popular"): return "popular"
|
|
||||||
|
|
||||||
def hide_advanced_search(self, *args, **kwargs):
|
|
||||||
self.lang.Hide()
|
|
||||||
self.resultstype.Hide()
|
|
||||||
|
|
||||||
def show_advanced_search(self, *args, **kwargs):
|
|
||||||
self.lang.Show()
|
|
||||||
self.resultstype.Show()
|
|
@ -1,28 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from . import baseDialog
|
|
||||||
|
|
||||||
class showUserProfile(baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(showUserProfile, self).__init__(parent=None, id=wx.ID_ANY)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
static = wx.StaticText(panel, -1, _(u"Details"))
|
|
||||||
sizer.Add(static, 0, wx.ALL, 5)
|
|
||||||
self.text = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE|wx.TE_READONLY, size=(350, 250))
|
|
||||||
self.text.SetFocus()
|
|
||||||
sizer.Add(self.text, 0, wx.ALL|wx.EXPAND, 5)
|
|
||||||
self.url = wx.Button(panel, -1, _(u"&Go to URL"), size=wx.DefaultSize)
|
|
||||||
self.url.Disable()
|
|
||||||
close = wx.Button(panel, wx.ID_CANCEL, _(u"&Close"))
|
|
||||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnSizer.Add(self.url, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(close, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def enable_url(self, enabled=True):
|
|
||||||
self.url.Enable(enabled)
|
|
@ -1,45 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from . import baseDialog
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class trendingTopicsDialog(baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(trendingTopicsDialog, self).__init__(None, -1)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.SetTitle(_(u"View trending topics"))
|
|
||||||
label = wx.StaticText(panel, wx.NewId(), _(u"Trending topics by"))
|
|
||||||
self.country = wx.RadioButton(panel, -1, _(u"Country"), style=wx.RB_GROUP)
|
|
||||||
self.city = wx.RadioButton(panel, -1, _(u"City"))
|
|
||||||
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
radioSizer.Add(label, 0, wx.ALL, 5)
|
|
||||||
radioSizer.Add(self.country, 0, wx.ALL, 5)
|
|
||||||
radioSizer.Add(self.city, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"&Location"))
|
|
||||||
self.location = wx.ListBox(panel, -1, choices=[], style=wx.CB_READONLY)
|
|
||||||
locationBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
locationBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
locationBox.Add(self.location, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(locationBox, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"&OK"))
|
|
||||||
ok.SetDefault()
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Close"))
|
|
||||||
btnsizer = wx.BoxSizer()
|
|
||||||
btnsizer.Add(ok, 0, wx.ALL, 5)
|
|
||||||
btnsizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def get_active(self):
|
|
||||||
if self.country.GetValue() == True:
|
|
||||||
return "country"
|
|
||||||
else:
|
|
||||||
return "city"
|
|
||||||
|
|
||||||
def get_item(self):
|
|
||||||
return self.location.GetStringSelection()
|
|
||||||
|
|
||||||
def set(self, values):
|
|
||||||
self.location.Set(values)
|
|
@ -1,2 +0,0 @@
|
|||||||
from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet, poll
|
|
||||||
from .templateDialogs import EditTemplateDialog
|
|
@ -1,542 +0,0 @@
|
|||||||
""" GUI dialogs for tweet writing and displaying. """
|
|
||||||
import wx
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
class tweet(wx.Dialog):
|
|
||||||
def __init__(self, title: str, caption: str, message: str = "", max_length: int = 280, thread_mode: bool = True, *args, **kwds) -> None:
|
|
||||||
""" Creates the basic Tweet dialog. This might be considered the base class for other dialogs.
|
|
||||||
title str: title to be used in the dialog.
|
|
||||||
caption str: This is the text to be placed alongside the text field.
|
|
||||||
message str: Text to be inserted in the tweet.
|
|
||||||
max_length int: Maximum amount of characters the tweet will accept. By default is 280 chahracters.
|
|
||||||
thread_mode bool: If set to False, disables the button that allows to make threads by adding more tweets.
|
|
||||||
"""
|
|
||||||
super(tweet, self).__init__(parent=None, *args, **kwds)
|
|
||||||
self.SetTitle(title)
|
|
||||||
self.create_controls(max_length=max_length, caption=caption, message=message, thread_mode=thread_mode)
|
|
||||||
|
|
||||||
def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None:
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
text_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(text_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_1 = wx.StaticText(panel, wx.ID_ANY, caption)
|
|
||||||
text_sizer.Add(label_1, 0, 0, 0)
|
|
||||||
self.text = wx.TextCtrl(panel, wx.ID_ANY, "", size=(444, -1), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
|
||||||
text_sizer.Add(self.text, 1, wx.EXPAND, 0)
|
|
||||||
list_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(list_sizer, 1, wx.EXPAND, 0)
|
|
||||||
Attachment_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments"))
|
|
||||||
Attachment_sizer.Add(label_2, 0, 0, 0)
|
|
||||||
self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
|
||||||
self.attachments.AppendColumn(_("File"))
|
|
||||||
self.attachments.AppendColumn(_("Type"))
|
|
||||||
self.attachments.AppendColumn(_("Description"))
|
|
||||||
Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
|
||||||
self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment"))
|
|
||||||
self.remove_attachment.Enable(False)
|
|
||||||
Attachment_sizer.Add(self.remove_attachment, 0, 0, 0)
|
|
||||||
tweet_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
list_sizer.Add(tweet_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_3 = wx.StaticText(panel, wx.ID_ANY, _("Added Tweets"))
|
|
||||||
tweet_sizer.Add(label_3, 0, 0, 0)
|
|
||||||
self.tweets = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
|
||||||
self.tweets.AppendColumn(_("Text"))
|
|
||||||
self.tweets.AppendColumn(_("Attachments"))
|
|
||||||
self.tweets.Enable(False)
|
|
||||||
tweet_sizer.Add(self.tweets, 1, wx.EXPAND, 0)
|
|
||||||
self.remove_tweet = wx.Button(panel, wx.ID_ANY, _("Delete tweet"))
|
|
||||||
self.remove_tweet.Enable(False)
|
|
||||||
tweet_sizer.Add(self.remove_tweet, 0, 0, 0)
|
|
||||||
btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0)
|
|
||||||
self.add = wx.Button(panel, wx.ID_ANY, _("A&dd..."))
|
|
||||||
btn_sizer_1.Add(self.add, 0, 0, 0)
|
|
||||||
self.add_tweet = wx.Button(panel, wx.ID_ANY, _("Add t&weet"))
|
|
||||||
self.add_tweet.Enable(thread_mode)
|
|
||||||
btn_sizer_1.Add(self.add_tweet, 0, 0, 0)
|
|
||||||
self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio..."))
|
|
||||||
btn_sizer_1.Add(self.add_audio, 0, 0, 0)
|
|
||||||
btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0)
|
|
||||||
self.autocomplete_users = wx.Button(panel, wx.ID_ANY, _("Auto&complete users"))
|
|
||||||
btn_sizer_2.Add(self.autocomplete_users, 0, 0, 0)
|
|
||||||
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
|
||||||
btn_sizer_2.Add(self.spellcheck, 0, 0, 0)
|
|
||||||
self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate"))
|
|
||||||
btn_sizer_2.Add(self.translate, 0, 0, 0)
|
|
||||||
ok_cancel_sizer = wx.StdDialogButtonSizer()
|
|
||||||
mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
|
||||||
self.send = wx.Button(panel, wx.ID_OK, _("Sen&d"))
|
|
||||||
self.send.SetDefault()
|
|
||||||
ok_cancel_sizer.Add(self.send, 0, 0, 0)
|
|
||||||
self.cancel = wx.Button(panel, wx.ID_CANCEL, "")
|
|
||||||
ok_cancel_sizer.AddButton(self.cancel)
|
|
||||||
ok_cancel_sizer.Realize()
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.Fit()
|
|
||||||
self.SetAffirmativeId(self.send.GetId())
|
|
||||||
self.SetEscapeId(self.cancel.GetId())
|
|
||||||
self.Layout()
|
|
||||||
|
|
||||||
def handle_keys(self, event: wx.Event, *args, **kwargs) -> None:
|
|
||||||
""" Allows to react to certain keyboard events from the text control. """
|
|
||||||
shift=event.ShiftDown()
|
|
||||||
if event.GetKeyCode() == wx.WXK_RETURN and shift==False and hasattr(self,'send'):
|
|
||||||
self.EndModal(wx.ID_OK)
|
|
||||||
else:
|
|
||||||
event.Skip()
|
|
||||||
|
|
||||||
def reset_controls(self) -> None:
|
|
||||||
""" Resetss text control and attachments to their default, empty values. This is used while adding more tweets in a thread. """
|
|
||||||
self.text.ChangeValue("")
|
|
||||||
self.attachments.DeleteAllItems()
|
|
||||||
|
|
||||||
def add_item(self, list_type: str = "attachment", item: List[str] = []) -> None:
|
|
||||||
""" Adds an item to a list control. Item should be a list with the same amount of items for each column present in the ListCtrl. """
|
|
||||||
if list_type == "attachment":
|
|
||||||
self.attachments.Append(item)
|
|
||||||
else:
|
|
||||||
self.tweets.Append(item)
|
|
||||||
|
|
||||||
def remove_item(self, list_type: str = "attachment") -> None:
|
|
||||||
if list_type == "attachment":
|
|
||||||
item = self.attachments.GetFocusedItem()
|
|
||||||
if item > -1:
|
|
||||||
self.attachments.DeleteItem(item)
|
|
||||||
else:
|
|
||||||
item = self.tweets.GetFocusedItem()
|
|
||||||
if item > -1:
|
|
||||||
self.tweets.DeleteItem(item)
|
|
||||||
|
|
||||||
def attach_menu(self, event=None, enabled=True, *args, **kwargs):
|
|
||||||
menu = wx.Menu()
|
|
||||||
self.add_image = menu.Append(wx.ID_ANY, _("Image"))
|
|
||||||
self.add_image.Enable(enabled)
|
|
||||||
self.add_video = menu.Append(wx.ID_ANY, _("Video"))
|
|
||||||
self.add_video.Enable(enabled)
|
|
||||||
self.add_poll = menu.Append(wx.ID_ANY, _("Poll"))
|
|
||||||
self.add_poll.Enable(enabled)
|
|
||||||
return menu
|
|
||||||
|
|
||||||
def ask_description(self):
|
|
||||||
dlg = wx.TextEntryDialog(self, _(u"please provide a description"), _(u"Description"))
|
|
||||||
dlg.ShowModal()
|
|
||||||
result = dlg.GetValue()
|
|
||||||
dlg.Destroy()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return (None, None)
|
|
||||||
dsc = self.ask_description()
|
|
||||||
return (openFileDialog.GetPath(), dsc)
|
|
||||||
|
|
||||||
def get_video(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _("Select the video to be uploaded"), "", "", _("Video files (*.mp4)|*.mp4"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return None
|
|
||||||
return openFileDialog.GetPath()
|
|
||||||
|
|
||||||
def unable_to_attach_file(self, *args, **kwargs):
|
|
||||||
return wx.MessageDialog(self, _("It is not possible to add more attachments. Please make sure your tweet complies with Twitter'S attachment rules. You can add only one video or GIF in every tweet, and a maximum of 4 photos."), _("Error adding attachment"), wx.ICON_ERROR).ShowModal()
|
|
||||||
|
|
||||||
class reply(tweet):
|
|
||||||
|
|
||||||
def __init__(self, users: List[str] = [], *args, **kwargs) -> None:
|
|
||||||
self.users = users
|
|
||||||
super(reply, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None:
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
text_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(text_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_1 = wx.StaticText(panel, wx.ID_ANY, caption)
|
|
||||||
text_sizer.Add(label_1, 0, 0, 0)
|
|
||||||
self.text = wx.TextCtrl(panel, wx.ID_ANY, "", size=(500, 200), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
|
||||||
text_sizer.Add(self.text, 1, wx.EXPAND, 0)
|
|
||||||
list_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(list_sizer, 1, wx.EXPAND, 0)
|
|
||||||
Attachment_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments"))
|
|
||||||
Attachment_sizer.Add(label_2, 0, 0, 0)
|
|
||||||
self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
|
||||||
self.attachments.AppendColumn(_("File"))
|
|
||||||
self.attachments.AppendColumn(_("Type"))
|
|
||||||
self.attachments.AppendColumn(_("Description"))
|
|
||||||
Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
|
||||||
self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment"))
|
|
||||||
self.remove_attachment.Enable(False)
|
|
||||||
Attachment_sizer.Add(self.remove_attachment, 0, 0, 0)
|
|
||||||
user_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
list_sizer.Add(user_sizer, 0, 0, 0)
|
|
||||||
self.mention_all = wx.CheckBox(panel, -1, _(u"&Mention to all"), size=wx.DefaultSize)
|
|
||||||
self.mention_all.Disable()
|
|
||||||
user_sizer.Add(self.mention_all, 0, wx.ALL, 5)
|
|
||||||
self.checkboxes = []
|
|
||||||
for i in self.users:
|
|
||||||
user_checkbox = wx.CheckBox(panel, -1, "@"+i, size=wx.DefaultSize)
|
|
||||||
self.checkboxes.append(user_checkbox)
|
|
||||||
user_sizer.Add(self.checkboxes[-1], 0, wx.ALL, 5)
|
|
||||||
btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0)
|
|
||||||
self.add = wx.Button(panel, wx.ID_ANY, _("A&dd..."))
|
|
||||||
btn_sizer_1.Add(self.add, 0, 0, 0)
|
|
||||||
self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio..."))
|
|
||||||
btn_sizer_1.Add(self.add_audio, 0, 0, 0)
|
|
||||||
btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0)
|
|
||||||
self.autocomplete_users = wx.Button(panel, wx.ID_ANY, _("Auto&complete users"))
|
|
||||||
btn_sizer_2.Add(self.autocomplete_users, 0, 0, 0)
|
|
||||||
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
|
||||||
btn_sizer_2.Add(self.spellcheck, 0, 0, 0)
|
|
||||||
self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate"))
|
|
||||||
btn_sizer_2.Add(self.translate, 0, 0, 0)
|
|
||||||
ok_cancel_sizer = wx.StdDialogButtonSizer()
|
|
||||||
mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
|
||||||
self.send = wx.Button(panel, wx.ID_OK, _("Sen&d"))
|
|
||||||
self.send.SetDefault()
|
|
||||||
ok_cancel_sizer.Add(self.send, 0, 0, 0)
|
|
||||||
self.cancel = wx.Button(panel, wx.ID_CANCEL, "")
|
|
||||||
ok_cancel_sizer.AddButton(self.cancel)
|
|
||||||
ok_cancel_sizer.Realize()
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.Fit()
|
|
||||||
self.SetAffirmativeId(self.send.GetId())
|
|
||||||
self.SetEscapeId(self.cancel.GetId())
|
|
||||||
self.Layout()
|
|
||||||
|
|
||||||
def attach_menu(self, event=None, enabled=True, *args, **kwargs):
|
|
||||||
menu = wx.Menu()
|
|
||||||
self.add_image = menu.Append(wx.ID_ANY, _("Image"))
|
|
||||||
self.add_image.Enable(enabled)
|
|
||||||
self.add_video = menu.Append(wx.ID_ANY, _("Video"))
|
|
||||||
self.add_video.Enable(enabled)
|
|
||||||
return menu
|
|
||||||
|
|
||||||
class dm(tweet):
|
|
||||||
|
|
||||||
def __init__(self, users: List[str] = [], *args, **kwargs) -> None:
|
|
||||||
self.users = users
|
|
||||||
super(dm, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None:
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
label_recipient = wx.StaticText(panel, -1, _("&Recipient"))
|
|
||||||
self.cb = wx.ComboBox(panel, -1, choices=self.users, value=self.users[0], size=wx.DefaultSize)
|
|
||||||
self.autocomplete_users = wx.Button(panel, -1, _(u"Auto&complete users"))
|
|
||||||
recipient_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
recipient_sizer.Add(label_recipient, 0, 0, 0)
|
|
||||||
recipient_sizer.Add(self.cb, 1, wx.EXPAND, 0)
|
|
||||||
mainBox.Add(recipient_sizer, 0, wx.EXPAND, 0)
|
|
||||||
text_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(text_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_1 = wx.StaticText(panel, wx.ID_ANY, caption)
|
|
||||||
text_sizer.Add(label_1, 0, 0, 0)
|
|
||||||
self.text = wx.TextCtrl(panel, wx.ID_ANY, "", size=(500, 200), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
|
||||||
self.text.SetFocus()
|
|
||||||
text_sizer.Add(self.text, 1, wx.EXPAND, 0)
|
|
||||||
list_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(list_sizer, 1, wx.EXPAND, 0)
|
|
||||||
Attachment_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments"))
|
|
||||||
Attachment_sizer.Add(label_2, 0, 0, 0)
|
|
||||||
self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
|
||||||
self.attachments.AppendColumn(_("File"))
|
|
||||||
self.attachments.AppendColumn(_("Type"))
|
|
||||||
self.attachments.AppendColumn(_("Description"))
|
|
||||||
Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
|
||||||
self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment"))
|
|
||||||
self.remove_attachment.Enable(False)
|
|
||||||
Attachment_sizer.Add(self.remove_attachment, 0, 0, 0)
|
|
||||||
btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0)
|
|
||||||
self.add = wx.Button(panel, wx.ID_ANY, _("A&dd..."))
|
|
||||||
btn_sizer_1.Add(self.add, 0, 0, 0)
|
|
||||||
self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio..."))
|
|
||||||
btn_sizer_1.Add(self.add_audio, 0, 0, 0)
|
|
||||||
btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0)
|
|
||||||
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
|
||||||
btn_sizer_2.Add(self.spellcheck, 0, 0, 0)
|
|
||||||
self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate"))
|
|
||||||
btn_sizer_2.Add(self.translate, 0, 0, 0)
|
|
||||||
ok_cancel_sizer = wx.StdDialogButtonSizer()
|
|
||||||
mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
|
||||||
self.send = wx.Button(panel, wx.ID_OK, _("Sen&d"))
|
|
||||||
self.send.SetDefault()
|
|
||||||
ok_cancel_sizer.Add(self.send, 0, 0, 0)
|
|
||||||
self.cancel = wx.Button(panel, wx.ID_CANCEL, "")
|
|
||||||
ok_cancel_sizer.AddButton(self.cancel)
|
|
||||||
ok_cancel_sizer.Realize()
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.Fit()
|
|
||||||
self.SetAffirmativeId(self.send.GetId())
|
|
||||||
self.SetEscapeId(self.cancel.GetId())
|
|
||||||
self.Layout()
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return (None, None)
|
|
||||||
return (openFileDialog.GetPath(), "")
|
|
||||||
|
|
||||||
def attach_menu(self, event=None, enabled=True, *args, **kwargs):
|
|
||||||
menu = wx.Menu()
|
|
||||||
self.add_image = menu.Append(wx.ID_ANY, _("Image"))
|
|
||||||
self.add_image.Enable(enabled)
|
|
||||||
self.add_video = menu.Append(wx.ID_ANY, _("Video"))
|
|
||||||
self.add_video.Enable(enabled)
|
|
||||||
return menu
|
|
||||||
|
|
||||||
class viewTweet(wx.Dialog):
|
|
||||||
def set_title(self, lenght):
|
|
||||||
self.SetTitle(_(u"Tweet - %i characters ") % (lenght,))
|
|
||||||
|
|
||||||
def __init__(self, text, rt_count, favs_count, source, date="", *args, **kwargs):
|
|
||||||
super(viewTweet, self).__init__(None, size=(850,850))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Tweet"))
|
|
||||||
self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
|
||||||
dc = wx.WindowDC(self.text)
|
|
||||||
dc.SetFont(self.text.GetFont())
|
|
||||||
(x, y) = dc.GetMultiLineTextExtent("W"*280)
|
|
||||||
self.text.SetSize((x, y))
|
|
||||||
self.text.SetFocus()
|
|
||||||
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
textBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
mainBox.Add(textBox, 0, wx.ALL, 5)
|
|
||||||
label2 = wx.StaticText(panel, -1, _(u"Image description"))
|
|
||||||
self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
|
||||||
dc = wx.WindowDC(self.image_description)
|
|
||||||
dc.SetFont(self.image_description.GetFont())
|
|
||||||
(x, y) = dc.GetMultiLineTextExtent("0"*450)
|
|
||||||
self.image_description.SetSize((x, y))
|
|
||||||
self.image_description.Enable(False)
|
|
||||||
iBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
iBox.Add(label2, 0, wx.ALL, 5)
|
|
||||||
iBox.Add(self.image_description, 1, wx.EXPAND, 5)
|
|
||||||
mainBox.Add(iBox, 0, wx.ALL, 5)
|
|
||||||
rtCountLabel = wx.StaticText(panel, -1, _(u"Retweets: "))
|
|
||||||
rtCount = wx.TextCtrl(panel, -1, rt_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
rtBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
rtBox.Add(rtCountLabel, 0, wx.ALL, 5)
|
|
||||||
rtBox.Add(rtCount, 0, wx.ALL, 5)
|
|
||||||
favsCountLabel = wx.StaticText(panel, -1, _(u"Likes: "))
|
|
||||||
favsCount = wx.TextCtrl(panel, -1, favs_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
favsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
favsBox.Add(favsCountLabel, 0, wx.ALL, 5)
|
|
||||||
favsBox.Add(favsCount, 0, wx.ALL, 5)
|
|
||||||
sourceLabel = wx.StaticText(panel, -1, _(u"Source: "))
|
|
||||||
sourceTweet = wx.TextCtrl(panel, -1, source, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
sourceBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sourceBox.Add(sourceLabel, 0, wx.ALL, 5)
|
|
||||||
sourceBox.Add(sourceTweet, 0, wx.ALL, 5)
|
|
||||||
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
|
||||||
dateTweet = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
dc = wx.WindowDC(dateTweet)
|
|
||||||
dc.SetFont(dateTweet.GetFont())
|
|
||||||
(x, y) = dc.GetTextExtent("0"*100)
|
|
||||||
dateTweet.SetSize((x, y))
|
|
||||||
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
|
||||||
dateBox.Add(dateTweet, 0, wx.ALL, 5)
|
|
||||||
infoBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
infoBox.Add(rtBox, 0, wx.ALL, 5)
|
|
||||||
infoBox.Add(favsBox, 0, wx.ALL, 5)
|
|
||||||
infoBox.Add(sourceBox, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(infoBox, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
|
||||||
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
|
||||||
self.share.Enable(False)
|
|
||||||
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
cancelButton.SetDefault()
|
|
||||||
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.SetClientSize(mainBox.CalcMin())
|
|
||||||
|
|
||||||
def set_text(self, text):
|
|
||||||
self.text.ChangeValue(text)
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
return self.text.GetValue()
|
|
||||||
|
|
||||||
def set_image_description(self, desc):
|
|
||||||
self.image_description.Enable(True)
|
|
||||||
if len(self.image_description.GetValue()) == 0:
|
|
||||||
self.image_description.SetValue(desc)
|
|
||||||
else:
|
|
||||||
self.image_description.SetValue(self.image_description.GetValue()+"\n"+desc)
|
|
||||||
|
|
||||||
def text_focus(self):
|
|
||||||
self.text.SetFocus()
|
|
||||||
|
|
||||||
def onSelect(self, ev):
|
|
||||||
self.text.SelectAll()
|
|
||||||
|
|
||||||
def enable_button(self, buttonName):
|
|
||||||
if hasattr(self, buttonName):
|
|
||||||
return getattr(self, buttonName).Enable()
|
|
||||||
|
|
||||||
class viewNonTweet(wx.Dialog):
|
|
||||||
|
|
||||||
def __init__(self, text, date="", *args, **kwargs):
|
|
||||||
super(viewNonTweet, self).__init__(None, size=(850,850))
|
|
||||||
self.SetTitle(_(u"View"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Item"))
|
|
||||||
self.text = wx.TextCtrl(parent=panel, id=-1, value=text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
|
||||||
dc = wx.WindowDC(self.text)
|
|
||||||
dc.SetFont(self.text.GetFont())
|
|
||||||
(x, y) = dc.GetMultiLineTextExtent("0"*140)
|
|
||||||
self.text.SetSize((x, y))
|
|
||||||
self.text.SetFocus()
|
|
||||||
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
textBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
mainBox.Add(textBox, 0, wx.ALL, 5)
|
|
||||||
if date != "":
|
|
||||||
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
|
||||||
date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
dc = wx.WindowDC(date)
|
|
||||||
dc.SetFont(date.GetFont())
|
|
||||||
(x, y) = dc.GetTextExtent("0"*100)
|
|
||||||
date.SetSize((x, y))
|
|
||||||
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
|
||||||
dateBox.Add(date, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
|
||||||
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
|
||||||
self.share.Enable(False)
|
|
||||||
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
cancelButton.SetDefault()
|
|
||||||
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.SetClientSize(mainBox.CalcMin())
|
|
||||||
|
|
||||||
def onSelect(self, ev):
|
|
||||||
self.text.SelectAll()
|
|
||||||
|
|
||||||
def set_text(self, text):
|
|
||||||
self.text.ChangeValue(text)
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
return self.text.GetValue()
|
|
||||||
|
|
||||||
def text_focus(self):
|
|
||||||
self.text.SetFocus()
|
|
||||||
|
|
||||||
def enable_button(self, buttonName):
|
|
||||||
if hasattr(self, buttonName):
|
|
||||||
return getattr(self, buttonName).Enable()
|
|
||||||
|
|
||||||
class poll(wx.Dialog):
|
|
||||||
def __init__(self, *args, **kwds):
|
|
||||||
super(poll, self).__init__(parent=None, id=wx.NewId(), title=_("Add a poll"))
|
|
||||||
sizer_1 = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
period_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizer_1.Add(period_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_period = wx.StaticText(self, wx.ID_ANY, _("Participation time (in days)"))
|
|
||||||
period_sizer.Add(label_period, 0, 0, 0)
|
|
||||||
self.period = wx.SpinCtrl(self, wx.ID_ANY)
|
|
||||||
self.period.SetFocus()
|
|
||||||
self.period.SetRange(1, 7)
|
|
||||||
self.period.SetValue(7)
|
|
||||||
period_sizer.Add(self.period, 0, 0, 0)
|
|
||||||
sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Choices")), wx.VERTICAL)
|
|
||||||
sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
|
|
||||||
option1_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizer_2.Add(option1_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_2 = wx.StaticText(self, wx.ID_ANY, _("Option 1"))
|
|
||||||
option1_sizer.Add(label_2, 0, 0, 0)
|
|
||||||
self.option1 = wx.TextCtrl(self, wx.ID_ANY, "")
|
|
||||||
self.option1.SetMaxLength(25)
|
|
||||||
option1_sizer.Add(self.option1, 0, 0, 0)
|
|
||||||
option2_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizer_2.Add(option2_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_3 = wx.StaticText(self, wx.ID_ANY, _("Option 2"))
|
|
||||||
option2_sizer.Add(label_3, 0, 0, 0)
|
|
||||||
self.option2 = wx.TextCtrl(self, wx.ID_ANY, "")
|
|
||||||
self.option2.SetMaxLength(25)
|
|
||||||
option2_sizer.Add(self.option2, 0, 0, 0)
|
|
||||||
option3_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizer_2.Add(option3_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_4 = wx.StaticText(self, wx.ID_ANY, _("Option 3"))
|
|
||||||
option3_sizer.Add(label_4, 0, 0, 0)
|
|
||||||
self.option3 = wx.TextCtrl(self, wx.ID_ANY, "")
|
|
||||||
self.option3.SetMaxLength(25)
|
|
||||||
option3_sizer.Add(self.option3, 0, 0, 0)
|
|
||||||
option4_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizer_2.Add(option4_sizer, 1, wx.EXPAND, 0)
|
|
||||||
label_5 = wx.StaticText(self, wx.ID_ANY, _("Option 4"))
|
|
||||||
option4_sizer.Add(label_5, 0, 0, 0)
|
|
||||||
self.option4 = wx.TextCtrl(self, wx.ID_ANY, "")
|
|
||||||
self.option4.SetMaxLength(25)
|
|
||||||
option4_sizer.Add(self.option4, 0, 0, 0)
|
|
||||||
btn_sizer = wx.StdDialogButtonSizer()
|
|
||||||
sizer_1.Add(btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
|
||||||
self.button_OK = wx.Button(self, wx.ID_OK)
|
|
||||||
self.button_OK.SetDefault()
|
|
||||||
self.button_OK.Bind(wx.EVT_BUTTON, self.validate_data)
|
|
||||||
btn_sizer.AddButton(self.button_OK)
|
|
||||||
self.button_CANCEL = wx.Button(self, wx.ID_CANCEL, "")
|
|
||||||
btn_sizer.AddButton(self.button_CANCEL)
|
|
||||||
btn_sizer.Realize()
|
|
||||||
self.SetSizer(sizer_1)
|
|
||||||
sizer_1.Fit(self)
|
|
||||||
self.SetAffirmativeId(self.button_OK.GetId())
|
|
||||||
self.SetEscapeId(self.button_CANCEL.GetId())
|
|
||||||
self.Layout()
|
|
||||||
|
|
||||||
def get_options(self):
|
|
||||||
controls = [self.option1, self.option2, self.option3, self.option4]
|
|
||||||
options = [option.GetValue() for option in controls if option.GetValue() != ""]
|
|
||||||
return options
|
|
||||||
|
|
||||||
def validate_data(self, *args, **kwargs):
|
|
||||||
options = self.get_options()
|
|
||||||
if len(options) < 2:
|
|
||||||
return wx.MessageDialog(self, _("Please make sure you have provided at least two options for the poll."), _("Not enough information"), wx.ICON_ERROR).ShowModal()
|
|
||||||
self.EndModal(wx.ID_OK)
|
|
@ -1,100 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from . import baseDialog
|
|
||||||
|
|
||||||
class updateProfileDialog(baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(updateProfileDialog, self).__init__(parent=None, id=-1)
|
|
||||||
self.SetTitle(_(u"Update your profile"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
labelName = wx.StaticText(panel, -1, _(u"&Name (50 characters maximum)"))
|
|
||||||
self.name = wx.TextCtrl(panel, -1)
|
|
||||||
self.name.SetFocus()
|
|
||||||
dc = wx.WindowDC(self.name)
|
|
||||||
dc.SetFont(self.name.GetFont())
|
|
||||||
self.name.SetSize(dc.GetTextExtent("0"*50))
|
|
||||||
labelLocation = wx.StaticText(panel, -1, _(u"&Location"))
|
|
||||||
self.location = wx.TextCtrl(panel, -1)
|
|
||||||
dc = wx.WindowDC(self.location)
|
|
||||||
dc.SetFont(self.location.GetFont())
|
|
||||||
self.location.SetSize(dc.GetTextExtent("0"*35))
|
|
||||||
labelUrl = wx.StaticText(panel, -1, _(u"&Website"))
|
|
||||||
self.url = wx.TextCtrl(panel, -1)
|
|
||||||
dc = wx.WindowDC(self.url)
|
|
||||||
dc.SetFont(self.url.GetFont())
|
|
||||||
self.url.SetSize(dc.GetTextExtent("0"*22))
|
|
||||||
labelDescription = wx.StaticText(panel, -1, _(u"&Bio (160 characters maximum)"))
|
|
||||||
self.description = wx.TextCtrl(panel, -1, size=(400, 400))
|
|
||||||
dc = wx.WindowDC(self.description)
|
|
||||||
dc.SetFont(self.description.GetFont())
|
|
||||||
self.description.SetSize(dc.GetTextExtent("0"*160))
|
|
||||||
self.image = None
|
|
||||||
self.upload_image = wx.Button(panel, -1, _(u"Upload a &picture"))
|
|
||||||
self.ok = wx.Button(panel, wx.ID_OK, _(u"&Update profile"))
|
|
||||||
self.ok.SetDefault()
|
|
||||||
close = wx.Button(panel, wx.ID_CANCEL, _("&Close"))
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
nameBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
nameBox.Add(labelName, 0, wx.ALL, 5)
|
|
||||||
nameBox.Add(self.name, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(nameBox, 0, wx.ALL, 5)
|
|
||||||
locationBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
locationBox.Add(labelLocation, 0, wx.ALL, 5)
|
|
||||||
locationBox.Add(self.location, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(locationBox, 0, wx.ALL, 5)
|
|
||||||
urlBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
urlBox.Add(labelUrl, 0, wx.ALL, 5)
|
|
||||||
urlBox.Add(self.url, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(urlBox, 0, wx.ALL, 5)
|
|
||||||
descriptionBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
descriptionBox.Add(labelDescription, 0, wx.ALL, 5)
|
|
||||||
descriptionBox.Add(self.description, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(descriptionBox, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.upload_image, 5, wx.CENTER, 5)
|
|
||||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
btnBox.Add(self.ok, 0, wx.ALL, 5)
|
|
||||||
btnBox.Add(close, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def set_name(self, name):
|
|
||||||
self.set("name", name)
|
|
||||||
|
|
||||||
def set_description(self, description):
|
|
||||||
self.set("description", description)
|
|
||||||
|
|
||||||
def set_location(self, location):
|
|
||||||
self.set("location", location)
|
|
||||||
|
|
||||||
def set_url(self, url):
|
|
||||||
self.set("url", url)
|
|
||||||
|
|
||||||
def change_upload_button(self, uploaded=False):
|
|
||||||
if uploaded == False:
|
|
||||||
self.upload_image.SetLabel(_(u"Upload a picture"))
|
|
||||||
else:
|
|
||||||
self.upload_image.SetLabel(_(u"Discard image"))
|
|
||||||
|
|
||||||
def upload_picture(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return None
|
|
||||||
return openFileDialog.GetPath()
|
|
||||||
|
|
||||||
def hide_upload_button(self, hide):
|
|
||||||
self.upload_image.Enable(hide)
|
|
||||||
|
|
||||||
def set_readonly(self):
|
|
||||||
self.name.style = wx.TE_READONLY
|
|
||||||
self.name.Refresh()
|
|
||||||
self.description.style = wx.TE_READONLY
|
|
||||||
self.description.Refresh()
|
|
||||||
self.location.style = wx.TE_READONLY
|
|
||||||
self.location.Refresh()
|
|
||||||
self.url.style = wx.TE_READONLY
|
|
||||||
self.url.Refresh()
|
|
||||||
self.hide_upload_button(False)
|
|
||||||
self.ok.Enable(False)
|
|
@ -1,90 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class UserActionsDialog(wx.Dialog):
|
|
||||||
def __init__(self, users=[], default="follow", *args, **kwargs):
|
|
||||||
super(UserActionsDialog, self).__init__(parent=None, *args, **kwargs)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
userSizer = wx.BoxSizer()
|
|
||||||
self.SetTitle(_(u"Action"))
|
|
||||||
userLabel = wx.StaticText(panel, -1, _(u"&User"))
|
|
||||||
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0])
|
|
||||||
self.cb.SetFocus()
|
|
||||||
self.autocompletion = wx.Button(panel, -1, _(u"&Autocomplete users"))
|
|
||||||
userSizer.Add(userLabel, 0, wx.ALL, 5)
|
|
||||||
userSizer.Add(self.cb, 0, wx.ALL, 5)
|
|
||||||
userSizer.Add(self.autocompletion, 0, wx.ALL, 5)
|
|
||||||
actionSizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
label2 = wx.StaticText(panel, -1, _(u"Action"))
|
|
||||||
self.follow = wx.RadioButton(panel, -1, _(u"&Follow"), name=_(u"Action"), style=wx.RB_GROUP)
|
|
||||||
self.unfollow = wx.RadioButton(panel, -1, _(u"U&nfollow"))
|
|
||||||
self.mute = wx.RadioButton(panel, -1, _(u"&Mute"))
|
|
||||||
self.unmute = wx.RadioButton(panel, -1, _(u"Unmu&te"))
|
|
||||||
self.block = wx.RadioButton(panel, -1, _(u"&Block"))
|
|
||||||
self.unblock = wx.RadioButton(panel, -1, _(u"Unbl&ock"))
|
|
||||||
self.reportSpam = wx.RadioButton(panel, -1, _(u"&Report as spam"))
|
|
||||||
self.ignore_client = wx.RadioButton(panel, -1, _(u"&Ignore tweets from this client"))
|
|
||||||
self.setup_default(default)
|
|
||||||
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
hSizer.Add(label2, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.follow, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.unfollow, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.mute, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.unmute, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.block, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.unblock, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.reportSpam, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.ignore_client, 0, wx.ALL, 5)
|
|
||||||
hSizer.Add(actionSizer, 0, wx.ALL, 5)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"&OK"))
|
|
||||||
ok.SetDefault()
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Close"))
|
|
||||||
btnsizer = wx.BoxSizer()
|
|
||||||
btnsizer.Add(ok)
|
|
||||||
btnsizer.Add(cancel)
|
|
||||||
sizer.Add(userSizer)
|
|
||||||
sizer.Add(hSizer, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
|
|
||||||
def get_action(self):
|
|
||||||
if self.follow.GetValue() == True: return "follow"
|
|
||||||
elif self.unfollow.GetValue() == True: return "unfollow"
|
|
||||||
elif self.mute.GetValue() == True: return "mute"
|
|
||||||
elif self.unmute.GetValue() == True: return "unmute"
|
|
||||||
elif self.reportSpam.GetValue() == True: return "report"
|
|
||||||
elif self.block.GetValue() == True: return "block"
|
|
||||||
elif self.unblock.GetValue() == True: return "unblock"
|
|
||||||
elif self.ignore_client.GetValue() == True: return "ignore_client"
|
|
||||||
|
|
||||||
def setup_default(self, default):
|
|
||||||
if default == "follow":
|
|
||||||
self.follow.SetValue(True)
|
|
||||||
elif default == "unfollow":
|
|
||||||
self.unfollow.SetValue(True)
|
|
||||||
elif default == "mute":
|
|
||||||
self.mute.SetValue(True)
|
|
||||||
elif default == "unmute":
|
|
||||||
self.unmute.SetValue(True)
|
|
||||||
elif default == "report":
|
|
||||||
self.reportSpam.SetValue(True)
|
|
||||||
elif default == "block":
|
|
||||||
self.block.SetValue(True)
|
|
||||||
elif default == "unblock":
|
|
||||||
self.unblock.SetValue(True)
|
|
||||||
elif default == "ignore_client":
|
|
||||||
self.ignore_client.SetValue(True)
|
|
||||||
|
|
||||||
def get_response(self):
|
|
||||||
return self.ShowModal()
|
|
||||||
|
|
||||||
def get_user(self):
|
|
||||||
return self.cb.GetValue()
|
|
||||||
|
|
||||||
def get_position(self):
|
|
||||||
return self.cb.GetPosition()
|
|
||||||
|
|
||||||
def popup_menu(self, menu):
|
|
||||||
self.PopupMenu(menu, self.cb.GetPosition())
|
|
@ -1,66 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class selectUserDialog(wx.Dialog):
|
|
||||||
def __init__(self, users=[], default="tweets", *args, **kwargs):
|
|
||||||
super(selectUserDialog, self).__init__(parent=None, *args, **kwargs)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
userSizer = wx.BoxSizer()
|
|
||||||
self.SetTitle(_(u"Timeline for %s") % (users[0]))
|
|
||||||
userLabel = wx.StaticText(panel, -1, _(u"User"))
|
|
||||||
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0])
|
|
||||||
self.cb.SetFocus()
|
|
||||||
self.autocompletion = wx.Button(panel, -1, _(u"&Autocomplete users"))
|
|
||||||
userSizer.Add(userLabel, 0, wx.ALL, 5)
|
|
||||||
userSizer.Add(self.cb, 0, wx.ALL, 5)
|
|
||||||
userSizer.Add(self.autocompletion, 0, wx.ALL, 5)
|
|
||||||
actionSizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
label2 = wx.StaticText(panel, -1, _(u"Buffer type"))
|
|
||||||
self.tweets = wx.RadioButton(panel, -1, _(u"&Tweets"), style=wx.RB_GROUP)
|
|
||||||
self.favourites = wx.RadioButton(panel, -1, _(u"&Likes"))
|
|
||||||
self.followers = wx.RadioButton(panel, -1, _(u"&Followers"))
|
|
||||||
self.friends = wx.RadioButton(panel, -1, _(u"F&riends"))
|
|
||||||
self.setup_default(default)
|
|
||||||
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
hSizer.Add(label2, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.tweets, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.favourites, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.followers, 0, wx.ALL, 5)
|
|
||||||
actionSizer.Add(self.friends, 0, wx.ALL, 5)
|
|
||||||
hSizer.Add(actionSizer, 0, wx.ALL, 5)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"&OK"))
|
|
||||||
ok.SetDefault()
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Close"))
|
|
||||||
btnsizer = wx.BoxSizer()
|
|
||||||
btnsizer.Add(ok)
|
|
||||||
btnsizer.Add(cancel)
|
|
||||||
sizer.Add(userSizer)
|
|
||||||
sizer.Add(hSizer, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
|
|
||||||
def get_action(self):
|
|
||||||
if self.tweets.GetValue() == True: return "tweets"
|
|
||||||
elif self.favourites.GetValue() == True: return "favourites"
|
|
||||||
elif self.followers.GetValue() == True: return "followers"
|
|
||||||
elif self.friends.GetValue() == True: return "friends"
|
|
||||||
|
|
||||||
def setup_default(self, default):
|
|
||||||
if default == "tweets":
|
|
||||||
self.tweets.SetValue(True)
|
|
||||||
elif default == "favourites":
|
|
||||||
self.favourites.SetValue(True)
|
|
||||||
|
|
||||||
def get_response(self):
|
|
||||||
return self.ShowModal()
|
|
||||||
|
|
||||||
def get_user(self):
|
|
||||||
return self.cb.GetValue()
|
|
||||||
|
|
||||||
def get_position(self):
|
|
||||||
return self.cb.GetPosition()
|
|
||||||
|
|
||||||
def popup_menu(self, menu):
|
|
||||||
self.PopupMenu(menu, self.cb.GetPosition())
|
|
@ -1,49 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
############################################################
|
|
||||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
############################################################
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
from . import baseDialog
|
|
||||||
|
|
||||||
class selectUserDialog(baseDialog.BaseWXDialog):
|
|
||||||
def __init__(self, title, users):
|
|
||||||
super(selectUserDialog, self).__init__(parent=None, id=wx.ID_ANY, title=title)
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
userSizer = wx.BoxSizer()
|
|
||||||
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0], size=wx.DefaultSize)
|
|
||||||
self.cb.SetFocus()
|
|
||||||
self.autocompletion = wx.Button(panel, -1, _(u"&Autocomplete users"))
|
|
||||||
userSizer.Add(wx.StaticText(panel, -1, _(u"User")), 0, wx.ALL, 5)
|
|
||||||
userSizer.Add(self.cb, 0, wx.ALL, 5)
|
|
||||||
userSizer.Add(self.autocompletion, 0, wx.ALL, 5)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
|
||||||
ok.SetDefault()
|
|
||||||
# ok.Bind(wx.EVT_BUTTON, self.onok)
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
|
|
||||||
btnsizer = wx.BoxSizer()
|
|
||||||
btnsizer.Add(ok, 0, wx.ALL, 5)
|
|
||||||
btnsizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(userSizer, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def get_user(self):
|
|
||||||
return self.cb.GetValue()
|
|
@ -1,104 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class basePanelMenu(wx.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
super(basePanelMenu, self).__init__()
|
|
||||||
self.retweet = wx.MenuItem(self, wx.ID_ANY, _(u"&Retweet"))
|
|
||||||
self.Append(self.retweet)
|
|
||||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
|
||||||
self.Append(self.reply)
|
|
||||||
self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Like"))
|
|
||||||
self.Append(self.fav)
|
|
||||||
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"&Unlike"))
|
|
||||||
self.Append(self.unfav)
|
|
||||||
self.openUrl = wx.MenuItem(self, wx.ID_ANY, _(u"&Open URL"))
|
|
||||||
self.Append(self.openUrl)
|
|
||||||
self.openInBrowser = wx.MenuItem(self, wx.ID_ANY, _(u"&Open in Twitter"))
|
|
||||||
self.Append(self.openInBrowser)
|
|
||||||
self.play = wx.MenuItem(self, wx.ID_ANY, _(u"&Play audio"))
|
|
||||||
self.Append(self.play)
|
|
||||||
self.view = wx.MenuItem(self, wx.ID_ANY, _(u"&Show tweet"))
|
|
||||||
self.Append(self.view)
|
|
||||||
self.copy = wx.MenuItem(self, wx.ID_ANY, _(u"&Copy to clipboard"))
|
|
||||||
self.Append(self.copy)
|
|
||||||
self.remove = wx.MenuItem(self, wx.ID_ANY, _(u"&Delete"))
|
|
||||||
self.Append(self.remove)
|
|
||||||
self.userActions = wx.MenuItem(self, wx.ID_ANY, _(u"&User actions..."))
|
|
||||||
self.Append(self.userActions)
|
|
||||||
|
|
||||||
class dmPanelMenu(wx.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
super(dmPanelMenu, self).__init__()
|
|
||||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
|
||||||
self.Append(self.reply)
|
|
||||||
self.openUrl = wx.MenuItem(self, wx.ID_ANY, _(u"&Open URL"))
|
|
||||||
self.Append(self.openUrl)
|
|
||||||
self.play = wx.MenuItem(self, wx.ID_ANY, _(u"&Play audio"))
|
|
||||||
self.Append(self.play)
|
|
||||||
self.view = wx.MenuItem(self, wx.ID_ANY, _(u"&Show direct message"))
|
|
||||||
self.Append(self.view)
|
|
||||||
self.copy = wx.MenuItem(self, wx.ID_ANY, _(u"&Copy to clipboard"))
|
|
||||||
self.Append(self.copy)
|
|
||||||
self.remove = wx.MenuItem(self, wx.ID_ANY, _(u"&Delete"))
|
|
||||||
self.Append(self.remove)
|
|
||||||
self.userActions = wx.MenuItem(self, wx.ID_ANY, _(u"&User actions..."))
|
|
||||||
self.Append(self.userActions)
|
|
||||||
|
|
||||||
class sentPanelMenu(wx.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
super(sentPanelMenu, self).__init__()
|
|
||||||
self.openUrl = wx.MenuItem(self, wx.ID_ANY, _(u"&Open URL"))
|
|
||||||
self.Append(self.openUrl)
|
|
||||||
self.openInBrowser = wx.MenuItem(self, wx.ID_ANY, _(u"&Open in Twitter"))
|
|
||||||
self.Append(self.openInBrowser)
|
|
||||||
self.play = wx.MenuItem(self, wx.ID_ANY, _(u"&Play audio"))
|
|
||||||
self.Append(self.play)
|
|
||||||
self.view = wx.MenuItem(self, wx.ID_ANY, _(u"&Show tweet"))
|
|
||||||
self.Append(self.view)
|
|
||||||
self.copy = wx.MenuItem(self, wx.ID_ANY, _(u"&Copy to clipboard"))
|
|
||||||
self.Append(self.copy)
|
|
||||||
self.remove = wx.MenuItem(self, wx.ID_ANY, _(u"&Delete"))
|
|
||||||
self.Append(self.remove)
|
|
||||||
|
|
||||||
class eventsPanelMenu(wx.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
super(eventsPanelMenu, self).__init__()
|
|
||||||
self.view = wx.MenuItem(self, wx.ID_ANY, _(u"&Show event"))
|
|
||||||
self.Append(self.view)
|
|
||||||
self.copy = wx.MenuItem(self, wx.ID_ANY, _(u"&Copy to clipboard"))
|
|
||||||
self.Append(self.copy)
|
|
||||||
self.remove = wx.MenuItem(self, wx.ID_ANY, _(u"&Delete"))
|
|
||||||
self.Append(self.remove)
|
|
||||||
|
|
||||||
class peoplePanelMenu(wx.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
super(peoplePanelMenu, self).__init__()
|
|
||||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Direct &message"))
|
|
||||||
self.Append(self.reply)
|
|
||||||
self.lists = wx.MenuItem(self, wx.ID_ANY, _(u"&View lists"))
|
|
||||||
self.Append(self.lists)
|
|
||||||
self.lists.Enable(False)
|
|
||||||
self.details = wx.MenuItem(self, wx.ID_ANY, _(u"Show user &profile"))
|
|
||||||
self.Append(self.details)
|
|
||||||
self.view = wx.MenuItem(self, wx.ID_ANY, _(u"&Show user"))
|
|
||||||
self.Append(self.view)
|
|
||||||
self.openInBrowser = wx.MenuItem(self, wx.ID_ANY, _(u"&Open in Twitter"))
|
|
||||||
self.Append(self.openInBrowser)
|
|
||||||
self.copy = wx.MenuItem(self, wx.ID_ANY, _(u"&Copy to clipboard"))
|
|
||||||
self.Append(self.copy)
|
|
||||||
self.userActions = wx.MenuItem(self, wx.ID_ANY, _(u"&User actions..."))
|
|
||||||
self.Append(self.userActions)
|
|
||||||
|
|
||||||
class trendsPanelMenu(wx.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
super(trendsPanelMenu, self).__init__()
|
|
||||||
self.search_topic = wx.MenuItem(self, wx.ID_ANY, _(u"Search topic"))
|
|
||||||
self.Append(self.search_topic)
|
|
||||||
self.tweetThisTrend = wx.MenuItem(self, wx.ID_ANY, _(u"&Tweet about this trend"))
|
|
||||||
self.Append(self.tweetThisTrend)
|
|
||||||
self.view = wx.MenuItem(self, wx.ID_ANY, _(u"&Show item"))
|
|
||||||
self.Append(self.view)
|
|
||||||
self.copy = wx.MenuItem(self, wx.ID_ANY, _(u"&Copy to clipboard"))
|
|
||||||
self.Append(self.copy)
|
|
@ -32,10 +32,10 @@ class SysTrayIcon(wx.adv.TaskBarIcon):
|
|||||||
icon=wx.Icon(os.path.join(paths.app_path(), "icon.ico"), wx.BITMAP_TYPE_ICO)
|
icon=wx.Icon(os.path.join(paths.app_path(), "icon.ico"), wx.BITMAP_TYPE_ICO)
|
||||||
self.SetIcon(icon, application.name)
|
self.SetIcon(icon, application.name)
|
||||||
self.menu=wx.Menu()
|
self.menu=wx.Menu()
|
||||||
self.tweet = self.menu.Append(wx.ID_ANY, _(u"Tweet"))
|
self.post = self.menu.Append(wx.ID_ANY, _(u"Post"))
|
||||||
self.global_settings = self.menu.Append(wx.ID_ANY, _(u"&Global settings"))
|
self.global_settings = self.menu.Append(wx.ID_ANY, _(u"&Global settings"))
|
||||||
self.account_settings = self.menu.Append(wx.ID_ANY, _(u"Account se&ttings"))
|
self.account_settings = self.menu.Append(wx.ID_ANY, _(u"Account se&ttings"))
|
||||||
self.update_profile = self.menu.Append(wx.ID_ANY, _(u"Update &profile"))
|
# self.update_profile = self.menu.Append(wx.ID_ANY, _(u"Update &profile"))
|
||||||
self.show_hide = self.menu.Append(wx.ID_ANY, _(u"&Show / hide"))
|
self.show_hide = self.menu.Append(wx.ID_ANY, _(u"&Show / hide"))
|
||||||
self.doc = self.menu.Append(wx.ID_ANY, _(u"&Documentation"))
|
self.doc = self.menu.Append(wx.ID_ANY, _(u"&Documentation"))
|
||||||
self.check_for_updates = self.menu.Append(wx.ID_ANY, _(u"Check for &updates"))
|
self.check_for_updates = self.menu.Append(wx.ID_ANY, _(u"Check for &updates"))
|
||||||
|
Loading…
Reference in New Issue
Block a user