mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-04-04 19:12:28 -04:00
390 lines
16 KiB
Python
390 lines
16 KiB
Python
# -*- 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/>.
|
|
#
|
|
############################################################
|
|
import wx
|
|
import gui.dialogs
|
|
import twitter
|
|
import webbrowser
|
|
import config
|
|
import sound
|
|
import url_shortener
|
|
import logging as original_logger
|
|
import output
|
|
import platform
|
|
import datetime
|
|
import menus
|
|
from twitter import prettydate
|
|
from multiplatform_widgets import widgets
|
|
from mysc import event
|
|
from mysc.thread_utils import call_threaded
|
|
from twython import TwythonError
|
|
log = original_logger.getLogger("buffers.base")
|
|
|
|
class basePanel(wx.Panel):
|
|
|
|
def bind_events(self):
|
|
self.Bind(event.MyEVT_OBJECT, self.update)
|
|
self.Bind(event.MyEVT_DELETED, self.Remove)
|
|
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.showMenu, self.list.list)
|
|
self.Bind(wx.EVT_LIST_KEY_DOWN, self.showMenuByKey, self.list.list)
|
|
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
|
|
if self.system == "Windows":
|
|
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.onFocus)
|
|
else:
|
|
self.list.list.Bind(wx.EVT_LISTBOX, self.onFocus)
|
|
|
|
def get_message(self, dialog=False):
|
|
if dialog == False: return " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db))
|
|
else:
|
|
list = self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)
|
|
return " ".join(list[1:-2])
|
|
|
|
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)
|
|
if self.system == "Windows":
|
|
self.list.set_windows_size(0, 30)
|
|
self.list.set_windows_size(1, 160)
|
|
self.list.set_windows_size(2, 55)
|
|
self.list.set_windows_size(3, 42)
|
|
self.list.set_size()
|
|
|
|
def __init__(self, parent, window, name_buffer, function=None, argumento=None, sound="", timeline=False):
|
|
if timeline == False:
|
|
self.type = "buffer"
|
|
elif timeline == True:
|
|
self.type = "timeline"
|
|
self.db = window.db
|
|
self.twitter = window.twitter
|
|
self.name_buffer = name_buffer
|
|
self.function = function
|
|
self.argumento = argumento
|
|
self.sound = sound
|
|
self.parent = window
|
|
self.compose_function = twitter.compose.compose_tweet
|
|
self.system = platform.system()
|
|
wx.Panel.__init__(self, parent)
|
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
|
self.create_list()
|
|
self.btn = wx.Button(self, -1, _(u"Tweet"))
|
|
self.btn.Bind(wx.EVT_BUTTON, self.post_status)
|
|
self.retweetBtn = wx.Button(self, -1, _(u"Retweet"))
|
|
self.retweetBtn.Bind(wx.EVT_BUTTON, self.onRetweet)
|
|
self.responseBtn = wx.Button(self, -1, _(u"Reply"))
|
|
self.responseBtn.Bind(wx.EVT_BUTTON, self.onResponse)
|
|
self.dmBtn = wx.Button(self, -1, _(u"Direct message"))
|
|
self.dmBtn.Bind(wx.EVT_BUTTON, self.onDm)
|
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
btnSizer.Add(self.btn, 0, wx.ALL, 5)
|
|
btnSizer.Add(self.retweetBtn, 0, wx.ALL, 5)
|
|
btnSizer.Add(self.responseBtn, 0, wx.ALL, 5)
|
|
btnSizer.Add(self.dmBtn, 0, wx.ALL, 5)
|
|
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
self.sizer.Add(self.list.list, 0, wx.ALL, 5)
|
|
self.bind_events()
|
|
self.SetSizer(self.sizer)
|
|
|
|
def remove_buffer(self):
|
|
if self.type == "timeline":
|
|
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this timeline?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
names = config.main["other_buffers"]["timelines"]
|
|
user = self.name_buffer
|
|
log.info(u"Deleting %s's timeline" % user)
|
|
if user in names:
|
|
names.remove(user)
|
|
self.db.settings.pop(user)
|
|
pos = self.db.settings["buffers"].index(user)
|
|
self.db.settings["buffers"].remove(user)
|
|
return pos
|
|
elif self.type == "buffer":
|
|
output.speak(_(u"This buffer is not a timeline; it can't be deleted."))
|
|
return None
|
|
|
|
def remove_invalid_buffer(self):
|
|
if self.type == "timeline":
|
|
names = config.main["other_buffers"]["timelines"]
|
|
user = self.name_buffer
|
|
log.info(u"Deleting %s's timeline" % user)
|
|
if user in names:
|
|
names.remove(user)
|
|
self.db.settings.pop(user)
|
|
pos = self.db.settings["buffers"].index(user)
|
|
self.db.settings["buffers"].remove(user)
|
|
return pos
|
|
|
|
def Remove(self, ev):
|
|
# try:
|
|
self.list.remove_item(ev.GetItem())
|
|
# except:
|
|
# log.error(u"Cannot delete the %s item from list " % str(ev.GetItem()))
|
|
|
|
def destroy_status(self, ev):
|
|
index = self.list.get_selected()
|
|
try:
|
|
self.twitter.twitter.destroy_status(id=self.db.settings[self.name_buffer][index]["id"])
|
|
self.db.settings[self.name_buffer].pop(index)
|
|
self.list.remove_item(index)
|
|
if index > 0:
|
|
self.list.select_item(index-1)
|
|
except:
|
|
sound.player.play("error.ogg")
|
|
|
|
def onFocus(self, ev):
|
|
if self.db.settings[self.name_buffer][self.list.get_selected()].has_key("retweeted_status"): tweet = self.db.settings[self.name_buffer][self.list.get_selected()]["retweeted_status"]
|
|
else: tweet = self.db.settings[self.name_buffer][self.list.get_selected()]
|
|
if config.main["general"]["relative_times"] == True:
|
|
# On windows we need only put the new date on the column, but under linux and mac it isn't possible.
|
|
if self.system == "Windows":
|
|
original_date = datetime.datetime.strptime(tweet["created_at"], "%a %b %d %H:%M:%S +0000 %Y")
|
|
date = original_date-datetime.timedelta(seconds=-self.db.settings["utc_offset"])
|
|
ts = prettydate(original_date)
|
|
self.list.list.SetStringItem(self.list.get_selected(), 2, ts)
|
|
else:
|
|
self.list.list.SetString(self.list.get_selected(), " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)))
|
|
if twitter.utils.is_audio(tweet):
|
|
sound.player.play("audio.ogg", False)
|
|
|
|
def start_streams(self):
|
|
if self.name_buffer == "sent":
|
|
num = twitter.starting.start_sent(self.db, self.twitter, self.name_buffer, self.function, param=self.argumento)
|
|
else:
|
|
# try:
|
|
if self.argumento != None:
|
|
num = twitter.starting.start_stream(self.db, self.twitter, self.name_buffer, self.function, param=self.argumento)
|
|
else:
|
|
num = twitter.starting.start_stream(self.db, self.twitter, self.name_buffer, self.function)
|
|
# except TwythonError:
|
|
# raise TwythonError
|
|
# self.parent.delete_invalid_timeline()
|
|
if self.sound != "" and num > 0 and self.name_buffer != "home_timeline" and self.name_buffer != "sent": sound.player.play(self.sound)
|
|
return num
|
|
|
|
def get_more_items(self):
|
|
if config.main["general"]["reverse_timelines"] == False:
|
|
last_id = self.db.settings[self.name_buffer][0]["id"]
|
|
else:
|
|
last_id = self.db.settings[self.name_buffer][-1]["id"]
|
|
try:
|
|
items = twitter.starting.get_more_items(self.function, self.twitter, count=config.main["general"]["max_tweets_per_call"], max_id=last_id, screen_name=self.argumento)
|
|
except TwythonError as e:
|
|
output.speak(e.message)
|
|
for i in items:
|
|
if config.main["general"]["reverse_timelines"] == False:
|
|
self.db.settings[self.name_buffer].insert(0, i)
|
|
else:
|
|
self.db.settings[self.name_buffer].append(i)
|
|
if config.main["general"]["reverse_timelines"] == False:
|
|
for i in items:
|
|
tweet = self.compose_function(i, self.db)
|
|
self.list.insert_item(True, *tweet)
|
|
else:
|
|
for i in items:
|
|
tweet = self.compose_function(i, self.db)
|
|
self.list.insert_item(False, *tweet)
|
|
output.speak(_(u"%s items retrieved") % (len(items)))
|
|
|
|
def put_items(self, num):
|
|
if self.list.get_count() == 0:
|
|
for i in self.db.settings[self.name_buffer]:
|
|
tweet = self.compose_function(i, self.db)
|
|
self.list.insert_item(False, *tweet)
|
|
self.set_list_position()
|
|
elif self.list.get_count() > 0:
|
|
if config.main["general"]["reverse_timelines"] == False:
|
|
for i in self.db.settings[self.name_buffer][:num]:
|
|
tweet = self.compose_function(i, self.db)
|
|
self.list.insert_item(False, *tweet)
|
|
else:
|
|
for i in self.db.settings[self.name_buffer][0:num]:
|
|
tweet = self.compose_function(i, self.db)
|
|
self.list.insert_item(True, *tweet)
|
|
|
|
def onDm(self, ev):
|
|
if self.name_buffer == "sent": return
|
|
if self.name_buffer == "direct_messages":
|
|
self.onResponse(ev)
|
|
else:
|
|
user = self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"]
|
|
dlg = gui.dialogs.message.dm(_("Direct message to %s") % (user,), _(u"New direct message"), "", self)
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
|
|
# dlg.Destroy()
|
|
if ev != None:
|
|
self.list.list.SetFocus()
|
|
|
|
def post_status(self, ev=None):
|
|
text = gui.dialogs.message.tweet(_(u"Write the tweet here"), _(u"Tweet"), "", self)
|
|
if text.ShowModal() == wx.ID_OK:
|
|
if text.image == None:
|
|
call_threaded(self.twitter.api_call, call_name="update_status", _sound="tweet_send.ogg", status=text.text.GetValue())
|
|
else:
|
|
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="tweet_send.ogg", status=text.text.GetValue(), media=text.file)
|
|
# text.Destroy()
|
|
if ev != None: self.list.list.SetFocus()
|
|
|
|
def onRetweet(self, ev):
|
|
if self.name_buffer != "direct_messages":
|
|
id=self.db.settings[self.name_buffer][self.list.get_selected()]["id"]
|
|
ask = wx.MessageDialog(self.parent, _(u"Would you like to add a comment to this tweet?"), _("Retweet"), wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
|
|
response = ask.ShowModal()
|
|
if response == wx.ID_YES:
|
|
dlg = gui.dialogs.message.retweet(_(u"Add your comment to the tweet"), _(u"Retweet"), u"“@%s: %s ”" % (self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"], self.db.settings[self.name_buffer][self.list.get_selected()]["text"]), self)
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
if dlg.image == None:
|
|
call_threaded(self.twitter.api_call, call_name="update_status", _sound="retweet_send.ogg", status=dlg.text.GetValue(), in_reply_to_status_id=dlg.in_reply_to)
|
|
else:
|
|
call_threaded(self.twitter.call_api, call_name="update_status_with_media", _sound="retweet_send.ogg", status=dlg.text.GetValue(), in_reply_to_status_id=text.in_reply_to, media=dlg.file)
|
|
# dlg.Destroy()
|
|
if ev != None:
|
|
self.list.list.SetFocus()
|
|
elif response == wx.ID_NO:
|
|
call_threaded(self.twitter.api_call, call_name="retweet", _sound="retweet_send.ogg", id=id)
|
|
if ev != None: self.list.list.SetFocus()
|
|
ask.Destroy()
|
|
|
|
def onResponse(self, ev):
|
|
if self.name_buffer == "sent": return
|
|
dlg = gui.dialogs.message.reply(_(u"Reply to %s") % (self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"]), _(u"Reply"), u"@%s " % (self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"]), self)
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
if dlg.image == None:
|
|
call_threaded(self.twitter.api_call, call_name="update_status", _sound="reply_send.ogg", in_reply_to_status_id=dlg.in_reply_to, status=dlg.text.GetValue())
|
|
else:
|
|
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=dlg.in_reply_to, status=dlg.text.GetValue(), media=dlg.file)
|
|
# dlg.Destroy()
|
|
if ev != None: self.list.list.SetFocus()
|
|
|
|
def update(self, ev):
|
|
data = ev.GetItem()
|
|
announce = ev.GetAnnounce()
|
|
if config.main["general"]["reverse_timelines"] == False: self.db.settings[self.name_buffer].append(data)
|
|
else: self.db.settings[self.name_buffer].insert(0, data)
|
|
tweet = self.compose_function(data, self.db)
|
|
self.list.insert_item(config.main["general"]["reverse_timelines"], *tweet)
|
|
if self.name_buffer not in config.main["other_buffers"]["muted_buffers"]:
|
|
if self.sound != "": sound.player.play(self.sound)
|
|
if announce != "": output.speak(announce)
|
|
if self.name_buffer in config.main["other_buffers"]["autoread_buffers"]:
|
|
output.speak(" ".join(tweet[:2]))
|
|
|
|
def interact(self, ev):
|
|
try:
|
|
if self.db.settings[self.name_buffer][self.list.get_selected()].has_key("retweeted_status"): tweet = self.db.settings[self.name_buffer][self.list.get_selected()]["retweeted_status"]
|
|
else: tweet = self.db.settings[self.name_buffer][self.list.get_selected()]
|
|
urls = twitter.utils.find_urls_in_text(tweet["text"])
|
|
except:
|
|
urls = []
|
|
if type(ev) is str: event = ev
|
|
else:
|
|
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
|
|
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
|
|
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
|
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
|
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
|
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "delete_item"
|
|
else:
|
|
ev.Skip()
|
|
return
|
|
if event == "audio" and len(urls) > 0:
|
|
if len(urls) == 1:
|
|
self.streamer(urls[0])
|
|
elif len(urls) > 1:
|
|
urlList = gui.dialogs.urlList.urlList(urls)
|
|
if urlList.ShowModal() == wx.ID_OK:
|
|
self.streamer(urls[urlList.lista.GetSelection()])
|
|
elif event == "url":
|
|
if len(urls) == 0: return
|
|
elif len(urls) == 1:
|
|
output.speak(_(u"Opening URL..."), True)
|
|
webbrowser.open(urls[0])
|
|
elif len(urls) > 1:
|
|
urlList = gui.dialogs.urlList.urlList(urls)
|
|
if urlList.ShowModal() == wx.ID_OK:
|
|
webbrowser.open_new_tab(urls[urlList.lista.GetSelection()])
|
|
elif event == "volume_down":
|
|
if config.main["sound"]["volume"] > 0.05:
|
|
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
|
|
sound.player.play("volume_changed.ogg", False)
|
|
if hasattr(self.parent, "audioStream"):
|
|
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
|
|
elif event == "volume_up":
|
|
if config.main["sound"]["volume"] < 0.95:
|
|
config.main["sound"]["volume"] = config.main["sound"]["volume"]+0.05
|
|
sound.player.play("volume_changed.ogg", False)
|
|
if hasattr(self.parent, "audioStream"):
|
|
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
|
|
elif event == "clear_list" and self.list.get_count() > 0:
|
|
dlg = wx.MessageDialog(self, _(u"Do you really want to empty this buffer? It's tweets will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
self.db.settings[self.name_buffer] = []
|
|
self.list.clear()
|
|
elif event == "delete_item":
|
|
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this message?"), _(u"Delete"), wx.ICON_QUESTION|wx.YES_NO)
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
self.destroy_status(wx.EVT_MENU)
|
|
else:
|
|
return
|
|
try:
|
|
ev.Skip()
|
|
except:
|
|
pass
|
|
|
|
def streamer(self, url):
|
|
if hasattr(self.parent, "audioStream"):
|
|
if self.parent.audioStream.stream.is_active() == 0:
|
|
output.speak(_(u"Playing..."))
|
|
self.parent.audioStream = sound.urlStream(url)
|
|
try:
|
|
self.parent.audioStream.prepare()
|
|
self.parent.audioStream.play()
|
|
except:
|
|
del self.parent.audioStream
|
|
output.speak(_(u"Unable to play audio."))
|
|
else:
|
|
output.speak(_(u"Audio stopped."))
|
|
self.parent.audioStream.stream.stop()
|
|
else:
|
|
output.speak(_(u"Playing..."))
|
|
self.parent.audioStream = sound.urlStream(url)
|
|
try:
|
|
self.parent.audioStream.prepare()
|
|
self.parent.audioStream.play()
|
|
except:
|
|
output.speak(_(u"Unable to play audio."))
|
|
del self.parent.audioStream
|
|
|
|
def set_list_position(self):
|
|
if config.main["general"]["reverse_timelines"] == False:
|
|
self.list.select_item(len(self.db.settings[self.name_buffer])-1)
|
|
else:
|
|
self.list.select_item(0)
|
|
|
|
def showMenu(self, ev):
|
|
if self.list.get_count() == 0: return
|
|
if self.name_buffer == "sent":
|
|
self.PopupMenu(menus.sentPanelMenu(self), ev.GetPosition())
|
|
else:
|
|
self.PopupMenu(menus.basePanelMenu(self), ev.GetPosition())
|
|
|
|
def showMenuByKey(self, ev):
|
|
if self.list.get_count() == 0: return
|
|
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
|
if self.name_buffer == "sent":
|
|
self.PopupMenu(menus.sentPanelMenu(self), self.list.list.GetPosition())
|
|
else:
|
|
self.PopupMenu(menus.basePanelMenu(self), self.list.list.GetPosition())
|