mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2024-11-26 12:53:12 -06:00
Merge pull request #496 from MCV-Software/mastodon
Initial Mastodon Support
This commit is contained in:
commit
768f0bc396
@ -1,4 +1,6 @@
|
|||||||
wxpython
|
wxpython
|
||||||
|
pytest
|
||||||
|
coverage
|
||||||
wheel
|
wheel
|
||||||
future
|
future
|
||||||
configobj
|
configobj
|
||||||
@ -29,6 +31,7 @@ backports.functools_lru_cache
|
|||||||
cx_freeze
|
cx_freeze
|
||||||
tweepy
|
tweepy
|
||||||
twitter-text-parser
|
twitter-text-parser
|
||||||
|
mastodon.py
|
||||||
pyenchant
|
pyenchant
|
||||||
sqlitedict
|
sqlitedict
|
||||||
cx-Logging
|
cx-Logging
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import base as base
|
from . import base as base
|
||||||
from . import twitter as twitter
|
from . import twitter as twitter
|
||||||
|
from . import mastodon as mastodon
|
@ -139,3 +139,6 @@ class Buffer(object):
|
|||||||
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def view_item(self):
|
||||||
|
pass
|
@ -1,2 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from .base import BaseBuffer
|
from .base import BaseBuffer
|
||||||
|
from .mentions import MentionsBuffer
|
||||||
|
from .conversations import ConversationBuffer, ConversationListBuffer
|
||||||
|
from .users import UserBuffer
|
@ -1,141 +1,498 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
""" Common logic to all buffers in TWBlue."""
|
import time
|
||||||
import logging
|
|
||||||
import wx
|
import wx
|
||||||
import output
|
|
||||||
import sound
|
|
||||||
import widgetUtils
|
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 controller.mastodon import messages
|
||||||
|
from sessions.mastodon import compose, utils, templates
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
|
from pubsub import pub
|
||||||
|
from extra import ocr
|
||||||
|
from wxUI import buffers, dialogs, commonMessageDialogs
|
||||||
|
from wxUI.dialogs.mastodon import menus
|
||||||
|
from wxUI.dialogs.mastodon import dialogs as mastodon_dialogs
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.base.base")
|
log = logging.getLogger("controller.buffers.mastodon.base")
|
||||||
|
|
||||||
class Buffer(object):
|
class BaseBuffer(base.Buffer):
|
||||||
""" A basic buffer object. This should be the base class for all other derived buffers."""
|
def __init__(self, parent, function, name, sessionObject, account, sound=None, compose_func="compose_post", *args, **kwargs):
|
||||||
|
super(BaseBuffer, self).__init__(parent, function, *args, **kwargs)
|
||||||
|
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||||
|
self.create_buffer(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 "-followers" in self.name or "-following" in self.name:
|
||||||
|
self.finished_timeline = False
|
||||||
|
|
||||||
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
|
def create_buffer(self, parent, name):
|
||||||
"""Inits the main controller for this buffer:
|
self.buffer = buffers.mastodon.basePanel(parent, name)
|
||||||
@ parent wx.Treebook object: Container where we will put this buffer.
|
|
||||||
@ function str or None: function to be called periodically and update items on this buffer.
|
|
||||||
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
|
|
||||||
"""
|
|
||||||
super(Buffer, self).__init__()
|
|
||||||
self.function = function
|
|
||||||
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
|
|
||||||
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
|
|
||||||
# Read more about compose functions in sessions/twitter/compose.py.
|
|
||||||
self.compose_function = None
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
|
|
||||||
self.buffer = None
|
|
||||||
# This should countains the account associated to this buffer.
|
|
||||||
self.account = ""
|
|
||||||
# This controls whether the start_stream function should be called when starting the program.
|
|
||||||
self.needs_init = True
|
|
||||||
# if this is set to False, the buffer will be ignored on the invisible interface.
|
|
||||||
self.invisible = False
|
|
||||||
# Control variable, used to track time of execution for calls to start_stream.
|
|
||||||
self.execution_time = 0
|
|
||||||
|
|
||||||
def clear_list(self):
|
def get_buffer_name(self):
|
||||||
pass
|
""" Get buffer name from a set of different techniques."""
|
||||||
|
# firstly let's take the easier buffers.
|
||||||
def get_event(self, ev):
|
basic_buffers = dict(home_timeline=_("Home"), local_timeline=_("Local"), federated_timeline=_("Federated"), mentions=_("Mentions"), bookmarks=_("Bookmarks"), direct_messages=_("Direct messages"), sent=_("Sent"), favorites=_("Favorites"), followers=_("Followers"), following=_("Following"), blocked=_("Blocked users"), muted=_("Muted users"), notifications=_("Notifications"))
|
||||||
""" Catch key presses in the WX interface and generate the corresponding event names."""
|
if self.name in list(basic_buffers.keys()):
|
||||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
|
return basic_buffers[self.name]
|
||||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
|
# Check user timelines
|
||||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
elif hasattr(self, "username"):
|
||||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
if "-timeline" in self.name:
|
||||||
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
return _(u"{username}'s timeline").format(username=self.username,)
|
||||||
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
elif "-followers" in self.name:
|
||||||
# Raise a Special event when pressed Shift+F10 because Wx==4.1.x does not seems to trigger this by itself.
|
return _(u"{username}'s followers").format(username=self.username,)
|
||||||
# See https://github.com/manuelcortez/TWBlue/issues/353
|
elif "-following" in self.name:
|
||||||
elif ev.GetKeyCode() == wx.WXK_F10 and ev.ShiftDown(): event = "show_menu"
|
return _(u"{username}'s following").format(username=self.username,)
|
||||||
else:
|
log.error("Error getting name for buffer %s" % (self.name,))
|
||||||
event = None
|
return _(u"Unknown buffer")
|
||||||
ev.Skip()
|
|
||||||
if event != None:
|
|
||||||
try:
|
|
||||||
### ToDo: Remove after WX fixes issue #353 in the widgets.
|
|
||||||
if event == "show_menu":
|
|
||||||
return self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
|
|
||||||
getattr(self, event)()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def volume_down(self):
|
|
||||||
""" Decreases volume by 5%"""
|
|
||||||
if self.session.settings["sound"]["volume"] > 0.0:
|
|
||||||
if self.session.settings["sound"]["volume"] <= 0.05:
|
|
||||||
self.session.settings["sound"]["volume"] = 0.0
|
|
||||||
else:
|
|
||||||
self.session.settings["sound"]["volume"] -=0.05
|
|
||||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
|
|
||||||
self.session.sound.play("volume_changed.ogg")
|
|
||||||
self.session.settings.write()
|
|
||||||
|
|
||||||
def volume_up(self):
|
|
||||||
""" Increases volume by 5%."""
|
|
||||||
if self.session.settings["sound"]["volume"] < 1.0:
|
|
||||||
if self.session.settings["sound"]["volume"] >= 0.95:
|
|
||||||
self.session.settings["sound"]["volume"] = 1.0
|
|
||||||
else:
|
|
||||||
self.session.settings["sound"]["volume"] +=0.05
|
|
||||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
|
|
||||||
self.session.sound.play("volume_changed.ogg")
|
|
||||||
self.session.settings.write()
|
|
||||||
|
|
||||||
def start_stream(self, mandatory=False, play_sound=True):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_more_items(self):
|
|
||||||
output.speak(_(u"This action is not supported for this buffer"), True)
|
|
||||||
|
|
||||||
def put_items_on_list(self, items):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_buffer(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove_item(self, item):
|
|
||||||
f = self.buffer.list.get_selected()
|
|
||||||
self.buffer.list.remove_item(item)
|
|
||||||
self.buffer.list.select_item(f)
|
|
||||||
|
|
||||||
def bind_events(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
return self.buffer
|
|
||||||
|
|
||||||
def get_message(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_list_position(self, reversed=False):
|
|
||||||
if reversed == False:
|
|
||||||
self.buffer.list.select_item(-1)
|
|
||||||
else:
|
|
||||||
self.buffer.list.select_item(0)
|
|
||||||
|
|
||||||
def reply(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def send_message(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def share_item(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def can_share(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def destroy_status(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def post_status(self, *args, **kwargs):
|
def post_status(self, *args, **kwargs):
|
||||||
|
title = _("Post")
|
||||||
|
caption = _("Write your post here")
|
||||||
|
post = messages.post(session=self.session, title=title, caption=caption)
|
||||||
|
response = post.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
post_data = post.get_data()
|
||||||
|
call_threaded(self.session.send_post, posts=post_data, visibility=post.get_visibility(), **kwargs)
|
||||||
|
if hasattr(post.message, "destroy"):
|
||||||
|
post.message.destroy()
|
||||||
|
|
||||||
|
def get_formatted_message(self):
|
||||||
|
return self.compose_function(self.get_item(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])[1]
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
post = self.get_item()
|
||||||
|
if post == None:
|
||||||
|
return
|
||||||
|
template = self.session.settings["templates"]["post"]
|
||||||
|
|
||||||
|
t = templates.render_post(post, template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
|
def start_stream(self, 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("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))
|
||||||
|
count = self.session.settings["general"]["max_posts_per_call"]
|
||||||
|
min_id = None
|
||||||
|
# toDo: Implement reverse timelines properly here.
|
||||||
|
if (self.name != "favorites" and self.name != "bookmarks") and self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||||
|
min_id = self.session.db[self.name][-1].id
|
||||||
|
try:
|
||||||
|
results = getattr(self.session.api, self.function)(min_id=min_id, limit=count, *self.args, **self.kwargs)
|
||||||
|
results.reverse()
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
number_of_items = self.session.order_buffer(self.name, results)
|
||||||
|
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||||
|
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
||||||
|
if "-timeline" in self.name:
|
||||||
|
self.username = self.session.db[self.name][0]["account"].username
|
||||||
|
self.finished_timeline = True
|
||||||
|
self.put_items_on_list(number_of_items)
|
||||||
|
if number_of_items > 0 and self.name != "sent_posts" 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:
|
||||||
|
post = self.session.db[self.name][-1]
|
||||||
|
else:
|
||||||
|
post = self.session.db[self.name][0]
|
||||||
|
output.speak(_("New post in {0}").format(self.get_buffer_name()))
|
||||||
|
output.speak(" ".join(self.compose_function(post, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])))
|
||||||
|
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(_("{0} new posts in {1}.").format(number_of_items, self.get_buffer_name()))
|
||||||
|
|
||||||
|
def get_more_items(self):
|
||||||
|
elements = []
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
max_id = self.session.db[self.name][0].id
|
||||||
|
else:
|
||||||
|
max_id = self.session.db[self.name][-1].id
|
||||||
|
try:
|
||||||
|
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
items_db = self.session.db[self.name]
|
||||||
|
for i in items:
|
||||||
|
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||||
|
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:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(True, *post)
|
||||||
|
else:
|
||||||
|
for i in items:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(False, *post)
|
||||||
|
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.kwargs.get("id") in self.session.settings["other_buffers"]["timelines"]:
|
||||||
|
self.session.settings["other_buffers"]["timelines"].remove(self.kwargs.get("id"))
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
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:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(False, *post)
|
||||||
|
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:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(False, *post)
|
||||||
|
else:
|
||||||
|
items = list_to_use[0:number_of_items]
|
||||||
|
items.reverse()
|
||||||
|
for i in items:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(True, *post)
|
||||||
|
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
|
||||||
|
|
||||||
|
def add_new_item(self, item):
|
||||||
|
post = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
self.buffer.list.insert_item(False, *post)
|
||||||
|
else:
|
||||||
|
self.buffer.list.insert_item(True, *post)
|
||||||
|
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(post[:2]), speech=self.session.settings["reporting"]["speech_reporting"], braille=self.session.settings["reporting"]["braille_reporting"])
|
||||||
|
|
||||||
|
def update_item(self, item, position):
|
||||||
|
post = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.list.SetItem(position, 1, post[1])
|
||||||
|
|
||||||
|
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.post)
|
||||||
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.share_item, self.buffer.boost)
|
||||||
|
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, widgetUtils.BUTTON_PRESSED, self.toggle_favorite, self.buffer.fav)
|
||||||
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.toggle_bookmark, self.buffer.bookmark)
|
||||||
|
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
|
||||||
|
menu = menus.base()
|
||||||
|
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.boost)
|
||||||
|
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, self.buffer.list.list.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_item(self):
|
||||||
|
index = self.buffer.list.get_selected()
|
||||||
|
if index > -1 and self.session.db.get(self.name) != None:
|
||||||
|
return self.session.db[self.name][index]
|
||||||
|
|
||||||
|
def can_share(self):
|
||||||
|
post = self.get_item()
|
||||||
|
if post.visibility == "direct":
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def reply(self, *args):
|
||||||
|
item = self.get_item()
|
||||||
|
visibility = item.visibility
|
||||||
|
if visibility == "direct":
|
||||||
|
title = _("Conversation with {}").format(item.account.username)
|
||||||
|
caption = _("Write your message here")
|
||||||
|
else:
|
||||||
|
title = _("Reply to {}").format(item.account.username)
|
||||||
|
caption = _("Write your reply here")
|
||||||
|
if item.reblog != None:
|
||||||
|
users = ["@{} ".format(user.acct) for user in item.reblog.mentions if user.id != self.session.db["user_id"]]
|
||||||
|
if item.reblog.account.acct != item.account.acct and "@{} ".format(item.reblog.account.acct) not in users:
|
||||||
|
users.append("@{} ".format(item.reblog.account.acct))
|
||||||
|
else:
|
||||||
|
users = ["@{} ".format(user.acct) for user in item.mentions if user.id != self.session.db["user_id"]]
|
||||||
|
if "@{} ".format(item.account.acct) not in users and item.account.id != self.session.db["user_id"]:
|
||||||
|
users.insert(0, "@{} ".format(item.account.acct))
|
||||||
|
users_str = "".join(users)
|
||||||
|
post = messages.post(session=self.session, title=title, caption=caption, text=users_str)
|
||||||
|
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
|
||||||
|
post.message.visibility.SetSelection(visibility_settings.get(visibility))
|
||||||
|
# Respect content warning settings.
|
||||||
|
if item.sensitive:
|
||||||
|
post.message.sensitive.SetValue(item.sensitive)
|
||||||
|
post.message.spoiler.ChangeValue(item.spoiler_text)
|
||||||
|
post.message.on_sensitivity_changed()
|
||||||
|
response = post.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
post_data = post.get_data()
|
||||||
|
call_threaded(self.session.send_post, reply_to=item.id, posts=post_data, visibility=post.get_visibility())
|
||||||
|
if hasattr(post.message, "destroy"):
|
||||||
|
post.message.destroy()
|
||||||
|
|
||||||
|
def send_message(self, *args, **kwargs):
|
||||||
|
item = self.get_item()
|
||||||
|
title = _("Conversation with {}").format(item.account.username)
|
||||||
|
caption = _("Write your message here")
|
||||||
|
if item.reblog != None:
|
||||||
|
users = ["@{} ".format(user.acct) for user in item.reblog.mentions if user.id != self.session.db["user_id"]]
|
||||||
|
if item.reblog.account.acct != item.account.acct and "@{} ".format(item.reblog.account.acct) not in users:
|
||||||
|
users.append("@{} ".format(item.reblog.account.acct))
|
||||||
|
else:
|
||||||
|
users = ["@{} ".format(user.acct) for user in item.mentions if user.id != self.session.db["user_id"]]
|
||||||
|
if item.account.acct not in users and item.account.id != self.session.db["user_id"]:
|
||||||
|
users.insert(0, "@{} ".format(item.account.acct))
|
||||||
|
users_str = "".join(users)
|
||||||
|
post = messages.post(session=self.session, title=title, caption=caption, text=users_str)
|
||||||
|
post.message.visibility.SetSelection(3)
|
||||||
|
if item.sensitive:
|
||||||
|
post.message.sensitive.SetValue(item.sensitive)
|
||||||
|
post.message.spoiler.ChangeValue(item.spoiler_text)
|
||||||
|
post.message.on_sensitivity_changed()
|
||||||
|
response = post.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
post_data = post.get_data()
|
||||||
|
call_threaded(self.session.send_post, posts=post_data, visibility="direct", reply_to=item.id)
|
||||||
|
if hasattr(post.message, "destroy"):
|
||||||
|
post.message.destroy()
|
||||||
|
|
||||||
|
def share_item(self, *args, **kwargs):
|
||||||
|
if self.can_share() == False:
|
||||||
|
return output.speak(_("This action is not supported on conversation posts."))
|
||||||
|
post = self.get_item()
|
||||||
|
id = post.id
|
||||||
|
if self.session.settings["general"]["boost_mode"] == "ask":
|
||||||
|
answer = mastodon_dialogs.boost_question()
|
||||||
|
if answer == True:
|
||||||
|
self._direct_boost(id)
|
||||||
|
else:
|
||||||
|
self._direct_boost(id)
|
||||||
|
|
||||||
|
def _direct_boost(self, id):
|
||||||
|
item = self.session.api_call(call_name="status_reblog", _sound="retweet_send.ogg", id=id)
|
||||||
|
|
||||||
|
def onFocus(self, *args, **kwargs):
|
||||||
|
post = self.get_item()
|
||||||
|
if self.session.settings["general"]["relative_times"] == True:
|
||||||
|
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
||||||
|
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_or_video(post):
|
||||||
|
self.session.sound.play("audio.ogg")
|
||||||
|
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
||||||
|
self.session.sound.play("image.ogg")
|
||||||
|
can_share = self.can_share()
|
||||||
|
pub.sendMessage("toggleShare", shareable=can_share)
|
||||||
|
self.buffer.boost.Enable(can_share)
|
||||||
|
|
||||||
|
def audio(self, url='', *args, **kwargs):
|
||||||
|
if sound.URLPlayer.player.is_playing():
|
||||||
|
return sound.URLPlayer.stop_audio()
|
||||||
|
item = self.get_item()
|
||||||
|
if item == None:
|
||||||
|
return
|
||||||
|
urls = utils.get_media_urls(item)
|
||||||
|
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.")
|
||||||
|
|
||||||
|
def url(self, url='', announce=True, *args, **kwargs):
|
||||||
|
if url == '':
|
||||||
|
post = self.get_item()
|
||||||
|
if post.reblog != None:
|
||||||
|
urls = utils.find_urls(post.reblog)
|
||||||
|
else:
|
||||||
|
urls = utils.find_urls(post)
|
||||||
|
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()
|
||||||
|
|
||||||
|
def destroy_status(self, *args, **kwargs):
|
||||||
|
index = self.buffer.list.get_selected()
|
||||||
|
item = self.session.db[self.name][index]
|
||||||
|
if item.account.id != self.session.db["user_id"] or item.reblog != None:
|
||||||
|
output.speak(_("You can delete only your own posts."))
|
||||||
|
return
|
||||||
|
answer = mastodon_dialogs.delete_post_dialog()
|
||||||
|
if answer == True:
|
||||||
|
items = self.session.db[self.name]
|
||||||
|
try:
|
||||||
|
self.session.api.status_delete(id=item.id)
|
||||||
|
items.pop(index)
|
||||||
|
self.buffer.list.remove_item(index)
|
||||||
|
except Exception as e:
|
||||||
|
self.session.sound.play("error.ogg")
|
||||||
|
self.session.db[self.name] = items
|
||||||
|
|
||||||
|
def user_details(self):
|
||||||
|
item = self.get_item()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def save_positions(self):
|
def get_item_url(self):
|
||||||
try:
|
post = self.get_item()
|
||||||
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
if post.reblog != None:
|
||||||
except AttributeError:
|
return post.reblog.url
|
||||||
|
return post.url
|
||||||
|
|
||||||
|
def open_in_browser(self, *args, **kwargs):
|
||||||
|
url = self.get_item_url()
|
||||||
|
output.speak(_("Opening item in web browser..."))
|
||||||
|
webbrowser.open(url)
|
||||||
|
|
||||||
|
def add_to_favorites(self):
|
||||||
|
item = self.get_item()
|
||||||
|
if item.reblog != None:
|
||||||
|
item = item.reblog
|
||||||
|
call_threaded(self.session.api_call, call_name="status_favourite", preexec_message=_("Adding to favorites..."), _sound="favourite.ogg", id=item.id)
|
||||||
|
|
||||||
|
def remove_from_favorites(self):
|
||||||
|
item = self.get_item()
|
||||||
|
if item.reblog != None:
|
||||||
|
item = item.reblog
|
||||||
|
call_threaded(self.session.api_call, call_name="status_unfavourite", preexec_message=_("Removing from favorites..."), _sound="favourite.ogg", id=item.id)
|
||||||
|
|
||||||
|
def toggle_favorite(self, *args, **kwargs):
|
||||||
|
item = self.get_item()
|
||||||
|
if item.reblog != None:
|
||||||
|
item = item.reblog
|
||||||
|
item = self.session.api.status(item.id)
|
||||||
|
if item.favourited == False:
|
||||||
|
call_threaded(self.session.api_call, call_name="status_favourite", preexec_message=_("Adding to favorites..."), _sound="favourite.ogg", id=item.id)
|
||||||
|
else:
|
||||||
|
call_threaded(self.session.api_call, call_name="status_unfavourite", preexec_message=_("Removing from favorites..."), _sound="favourite.ogg", id=item.id)
|
||||||
|
|
||||||
|
def toggle_bookmark(self, *args, **kwargs):
|
||||||
|
item = self.get_item()
|
||||||
|
if item.reblog != None:
|
||||||
|
item = item.reblog
|
||||||
|
item = self.session.api.status(item.id)
|
||||||
|
if item.bookmarked == False:
|
||||||
|
call_threaded(self.session.api_call, call_name="status_bookmark", preexec_message=_("Adding to bookmarks..."), _sound="favourite.ogg", id=item.id)
|
||||||
|
else:
|
||||||
|
call_threaded(self.session.api_call, call_name="status_unbookmark", preexec_message=_("Removing from bookmarks..."), _sound="favourite.ogg", id=item.id)
|
||||||
|
|
||||||
|
def view_item(self):
|
||||||
|
post = self.get_item()
|
||||||
|
# Update object so we can retrieve newer stats
|
||||||
|
post = self.session.api.status(id=post.id)
|
||||||
|
print(post)
|
||||||
|
msg = messages.viewPost(post, offset_hours=self.session.db["utc_offset"], item_url=self.get_item_url())
|
||||||
|
|
||||||
|
def ocr_image(self):
|
||||||
|
post = self.get_item()
|
||||||
|
media_list = []
|
||||||
pass
|
pass
|
241
src/controller/buffers/mastodon/conversations.py
Normal file
241
src/controller/buffers/mastodon/conversations.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
import output
|
||||||
|
from controller.mastodon import messages
|
||||||
|
from controller.buffers.mastodon.base import BaseBuffer
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
|
from sessions.mastodon import utils, templates
|
||||||
|
from wxUI import buffers, commonMessageDialogs
|
||||||
|
log = logging.getLogger("controller.buffers.mastodon.conversations")
|
||||||
|
|
||||||
|
class ConversationListBuffer(BaseBuffer):
|
||||||
|
|
||||||
|
def create_buffer(self, parent, name):
|
||||||
|
self.buffer = buffers.mastodon.conversationListPanel(parent, name)
|
||||||
|
|
||||||
|
def get_item(self):
|
||||||
|
index = self.buffer.list.get_selected()
|
||||||
|
if index > -1 and self.session.db.get(self.name) != None and len(self.session.db[self.name]) > index:
|
||||||
|
return self.session.db[self.name][index]["last_status"]
|
||||||
|
|
||||||
|
def get_conversation(self):
|
||||||
|
index = self.buffer.list.get_selected()
|
||||||
|
if index > -1 and self.session.db.get(self.name) != None:
|
||||||
|
return self.session.db[self.name][index]
|
||||||
|
|
||||||
|
def get_formatted_message(self):
|
||||||
|
return self.compose_function(self.get_conversation(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])[1]
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
conversation = self.get_conversation()
|
||||||
|
if conversation == None:
|
||||||
|
return
|
||||||
|
template = self.session.settings["templates"]["conversation"]
|
||||||
|
post_template = self.session.settings["templates"]["post"]
|
||||||
|
t = templates.render_conversation(conversation=conversation, template=template, post_template=post_template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
|
def start_stream(self, 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("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))
|
||||||
|
count = self.session.settings["general"]["max_posts_per_call"]
|
||||||
|
min_id = None
|
||||||
|
# toDo: Implement reverse timelines properly here.
|
||||||
|
# if (self.name != "favorites" and self.name != "bookmarks") and self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||||
|
# min_id = self.session.db[self.name][-1].id
|
||||||
|
try:
|
||||||
|
results = getattr(self.session.api, self.function)(min_id=min_id, limit=count, *self.args, **self.kwargs)
|
||||||
|
results.reverse()
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
new_position, number_of_items = self.order_buffer(results)
|
||||||
|
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||||
|
self.put_items_on_list(number_of_items)
|
||||||
|
if new_position > -1:
|
||||||
|
self.buffer.list.select_item(new_position)
|
||||||
|
if number_of_items > 0 and self.name != "sent_posts" 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 get_more_items(self):
|
||||||
|
elements = []
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
max_id = self.session.db[self.name][0].last_status.id
|
||||||
|
else:
|
||||||
|
max_id = self.session.db[self.name][-1].last_status.id
|
||||||
|
try:
|
||||||
|
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
items_db = self.session.db[self.name]
|
||||||
|
for i in items:
|
||||||
|
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||||
|
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:
|
||||||
|
conversation = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(True, *conversation)
|
||||||
|
else:
|
||||||
|
for i in items:
|
||||||
|
conversation = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(False, *conversation)
|
||||||
|
self.buffer.list.select_item(selection)
|
||||||
|
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
||||||
|
|
||||||
|
def get_item_position(self, conversation):
|
||||||
|
for i in range(len(self.session.db[self.name])):
|
||||||
|
if self.session.db[self.name][i].id == conversation.id:
|
||||||
|
return i
|
||||||
|
|
||||||
|
def order_buffer(self, data):
|
||||||
|
num = 0
|
||||||
|
focus_object = None
|
||||||
|
if self.session.db.get(self.name) == None:
|
||||||
|
self.session.db[self.name] = []
|
||||||
|
objects = self.session.db[self.name]
|
||||||
|
for i in data:
|
||||||
|
position = self.get_item_position(i)
|
||||||
|
if position != None:
|
||||||
|
conversation = self.session.db[self.name][position]
|
||||||
|
if conversation.last_status.id != i.last_status.id:
|
||||||
|
focus_object = i
|
||||||
|
objects.pop(position)
|
||||||
|
self.buffer.list.remove_item(position)
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
objects.append(i)
|
||||||
|
else:
|
||||||
|
objects.insert(0, i)
|
||||||
|
num = num+1
|
||||||
|
else:
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
objects.append(i)
|
||||||
|
else:
|
||||||
|
objects.insert(0, i)
|
||||||
|
num = num+1
|
||||||
|
self.session.db[self.name] = objects
|
||||||
|
if focus_object == None:
|
||||||
|
return (-1, num)
|
||||||
|
new_position = self.get_item_position(focus_object)
|
||||||
|
if new_position != None:
|
||||||
|
return (new_position, num)
|
||||||
|
return (-1, num)
|
||||||
|
|
||||||
|
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.post)
|
||||||
|
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 fav(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unfav(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def can_share(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_message(self):
|
||||||
|
return self.reply()
|
||||||
|
|
||||||
|
def onFocus(self, *args, **kwargs):
|
||||||
|
post = self.get_item()
|
||||||
|
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
||||||
|
self.session.sound.play("audio.ogg")
|
||||||
|
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
||||||
|
self.session.sound.play("image.ogg")
|
||||||
|
|
||||||
|
def destroy_status(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reply(self, *args):
|
||||||
|
item = self.get_item()
|
||||||
|
conversation = self.get_conversation()
|
||||||
|
visibility = item.visibility
|
||||||
|
title = _("Reply to conversation with {}").format(conversation.accounts[0].username)
|
||||||
|
caption = _("Write your message here")
|
||||||
|
users = ["@{} ".format(user.acct) for user in conversation.accounts]
|
||||||
|
users_str = "".join(users)
|
||||||
|
post = messages.post(session=self.session, title=title, caption=caption, text=users_str)
|
||||||
|
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
|
||||||
|
post.message.visibility.SetSelection(visibility_settings.get(visibility))
|
||||||
|
if item.sensitive:
|
||||||
|
post.message.sensitive.SetValue(item.sensitive)
|
||||||
|
post.message.spoiler.ChangeValue(item.spoiler_text)
|
||||||
|
post.message.on_sensitivity_changed()
|
||||||
|
response = post.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
post_data = post.get_data()
|
||||||
|
call_threaded(self.session.send_post, reply_to=item.id, posts=post_data, visibility=visibility)
|
||||||
|
if hasattr(post.message, "destroy"):
|
||||||
|
post.message.destroy()
|
||||||
|
|
||||||
|
class ConversationBuffer(BaseBuffer):
|
||||||
|
|
||||||
|
def __init__(self, post, *args, **kwargs):
|
||||||
|
self.post = post
|
||||||
|
super(ConversationBuffer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def start_stream(self, 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("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))
|
||||||
|
self.post = self.session.api.status(id=self.post.id)
|
||||||
|
# toDo: Implement reverse timelines properly here.
|
||||||
|
try:
|
||||||
|
results = []
|
||||||
|
items = getattr(self.session.api, self.function)(*self.args, **self.kwargs)
|
||||||
|
[results.append(item) for item in items.ancestors]
|
||||||
|
results.append(self.post)
|
||||||
|
[results.append(item) for item in items.descendants]
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
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.name != "sent_posts" 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 get_more_items(self):
|
||||||
|
output.speak(_(u"This action is not supported for this buffer"), True)
|
||||||
|
|
||||||
|
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
|
71
src/controller/buffers/mastodon/mentions.py
Normal file
71
src/controller/buffers/mastodon/mentions.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
from controller.buffers.mastodon.base import BaseBuffer
|
||||||
|
from sessions.mastodon import utils
|
||||||
|
|
||||||
|
log = logging.getLogger("controller.buffers.mastodon.mentions")
|
||||||
|
|
||||||
|
class MentionsBuffer(BaseBuffer):
|
||||||
|
|
||||||
|
def start_stream(self, 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("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))
|
||||||
|
count = self.session.settings["general"]["max_posts_per_call"]
|
||||||
|
min_id = None
|
||||||
|
# toDo: Implement reverse timelines properly here.
|
||||||
|
# if self.name != "favorites" and self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||||
|
# min_id = self.session.db[self.name][-1].id
|
||||||
|
try:
|
||||||
|
items = getattr(self.session.api, self.function)(min_id=min_id, limit=count, exclude_types=["follow", "favourite", "reblog", "poll", "follow_request"], *self.args, **self.kwargs)
|
||||||
|
items.reverse()
|
||||||
|
results = [item.status for item in items if item.get("status") and item.type == "mention"]
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
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.name != "sent_posts" 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 get_more_items(self):
|
||||||
|
elements = []
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
max_id = self.session.db[self.name][0].id
|
||||||
|
else:
|
||||||
|
max_id = self.session.db[self.name][-1].id
|
||||||
|
try:
|
||||||
|
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], exclude_types=["follow", "favourite", "reblog", "poll", "follow_request"], *self.args, **self.kwargs)
|
||||||
|
items = [item.status for item in items if item.get("status") and item.type == "mention"]
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
items_db = self.session.db[self.name]
|
||||||
|
for i in items:
|
||||||
|
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||||
|
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:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(True, *post)
|
||||||
|
else:
|
||||||
|
for i in items:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(False, *post)
|
||||||
|
self.buffer.list.select_item(selection)
|
||||||
|
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
202
src/controller/buffers/mastodon/users.py
Normal file
202
src/controller/buffers/mastodon/users.py
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
import output
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
|
from controller.buffers.mastodon.base import BaseBuffer
|
||||||
|
from controller.mastodon import messages
|
||||||
|
from sessions.mastodon import templates, utils
|
||||||
|
from wxUI import buffers, commonMessageDialogs
|
||||||
|
|
||||||
|
log = logging.getLogger("controller.buffers.mastodon.conversations")
|
||||||
|
|
||||||
|
class UserBuffer(BaseBuffer):
|
||||||
|
|
||||||
|
def create_buffer(self, parent, name):
|
||||||
|
self.buffer = buffers.mastodon.userPanel(parent, name)
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
user = self.get_item()
|
||||||
|
if user == None:
|
||||||
|
return
|
||||||
|
template = self.session.settings["templates"]["person"]
|
||||||
|
t = templates.render_user(user=user, template=template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
|
def bind_events(self):
|
||||||
|
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.post)
|
||||||
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.send_message, self.buffer.message)
|
||||||
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.user_actions, self.buffer.actions)
|
||||||
|
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 fav(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unfav(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def can_share(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reply(self, *args, **kwargs):
|
||||||
|
return self.send_message()
|
||||||
|
|
||||||
|
def send_message(self, *args, **kwargs):
|
||||||
|
item = self.get_item()
|
||||||
|
title = _("New conversation with {}").format(item.username)
|
||||||
|
caption = _("Write your message here")
|
||||||
|
users_str = "@{} ".format(item.acct)
|
||||||
|
post = messages.post(session=self.session, title=title, caption=caption, text=users_str)
|
||||||
|
post.message.visibility.SetSelection(3)
|
||||||
|
response = post.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
post_data = post.get_data()
|
||||||
|
call_threaded(self.session.send_post, posts=post_data, visibility="direct")
|
||||||
|
if hasattr(post.message, "destroy"):
|
||||||
|
post.message.destroy()
|
||||||
|
|
||||||
|
def audio(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def url(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def destroy_status(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def start_stream(self, 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("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))
|
||||||
|
count = self.session.settings["general"]["max_posts_per_call"]
|
||||||
|
# toDo: Implement reverse timelines properly here.
|
||||||
|
try:
|
||||||
|
results = getattr(self.session.api, self.function)(limit=count, *self.args, **self.kwargs)
|
||||||
|
if hasattr(results, "_pagination_next") and self.name not in self.session.db["pagination_info"]:
|
||||||
|
self.session.db["pagination_info"][self.name] = results._pagination_next
|
||||||
|
results.reverse()
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
number_of_items = self.session.order_buffer(self.name, results)
|
||||||
|
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||||
|
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
||||||
|
if "-followers" in self.name or "-following" in self.name:
|
||||||
|
self.username = self.session.api.account(id=self.kwargs.get("id")).username
|
||||||
|
self.finished_timeline = True
|
||||||
|
self.put_items_on_list(number_of_items)
|
||||||
|
if number_of_items > 0 and self.name != "sent_posts" 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 get_more_items(self):
|
||||||
|
elements = []
|
||||||
|
pagination_info = self.session.db["pagination_info"].get(self.name)
|
||||||
|
if pagination_info == None:
|
||||||
|
output.speak(_("There are no more items in this buffer."))
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
items = self.session.api.fetch_next(pagination_info)
|
||||||
|
if hasattr(items, "_pagination_next"):
|
||||||
|
self.session.db["pagination_info"][self.name] = items._pagination_next
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error %s" % (str(e)))
|
||||||
|
return
|
||||||
|
items_db = self.session.db[self.name]
|
||||||
|
for i in items:
|
||||||
|
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||||
|
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:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(True, *post)
|
||||||
|
else:
|
||||||
|
for i in items:
|
||||||
|
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
|
||||||
|
self.buffer.list.insert_item(False, *post)
|
||||||
|
self.buffer.list.select_item(selection)
|
||||||
|
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
||||||
|
|
||||||
|
def get_item_url(self):
|
||||||
|
item = self.get_item()
|
||||||
|
return item.url
|
||||||
|
|
||||||
|
def user_details(self):
|
||||||
|
item = self.get_item()
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_to_favorites(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_from_favorites(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def toggle_favorite(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def view_item(self):
|
||||||
|
item = self.get_item()
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
def ocr_image(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_buffer(self, force=False):
|
||||||
|
if "-followers" in self.name:
|
||||||
|
if force == False:
|
||||||
|
dlg = commonMessageDialogs.remove_buffer()
|
||||||
|
else:
|
||||||
|
dlg = widgetUtils.YES
|
||||||
|
if dlg == widgetUtils.YES:
|
||||||
|
if self.kwargs.get("id") in self.session.settings["other_buffers"]["followers_timelines"]:
|
||||||
|
self.session.settings["other_buffers"]["followers_timelines"].remove(self.kwargs.get("id"))
|
||||||
|
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 "-following" in self.name:
|
||||||
|
if force == False:
|
||||||
|
dlg = commonMessageDialogs.remove_buffer()
|
||||||
|
else:
|
||||||
|
dlg = widgetUtils.YES
|
||||||
|
if dlg == widgetUtils.YES:
|
||||||
|
if self.kwargs.get("id") in self.session.settings["other_buffers"]["following_timelines"]:
|
||||||
|
self.session.settings["other_buffers"]["following_timelines"].remove(self.kwargs.get("id"))
|
||||||
|
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 "-searchUser" in self.name:
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
|
||||||
|
return False
|
@ -1,9 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import time
|
import time
|
||||||
import wx
|
import wx
|
||||||
from wxUI import buffers, dialogs, commonMessageDialogs, menus
|
|
||||||
from controller import user
|
|
||||||
from controller import messages
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import arrow
|
import arrow
|
||||||
import webbrowser
|
import webbrowser
|
||||||
@ -19,7 +16,10 @@ from mysc.thread_utils import call_threaded
|
|||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from tweepy.cursor import Cursor
|
from tweepy.cursor import Cursor
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
from extra import ocr
|
||||||
from sessions.twitter.long_tweets import twishort, tweets
|
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")
|
log = logging.getLogger("controller.buffers")
|
||||||
|
|
||||||
@ -35,9 +35,9 @@ class BaseBuffer(base.Buffer):
|
|||||||
super(BaseBuffer, self).__init__(parent, function, *args, **kwargs)
|
super(BaseBuffer, self).__init__(parent, function, *args, **kwargs)
|
||||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||||
if bufferType != None:
|
if bufferType != None:
|
||||||
self.buffer = getattr(buffers, bufferType)(parent, name)
|
self.buffer = getattr(buffers.twitter, bufferType)(parent, name)
|
||||||
else:
|
else:
|
||||||
self.buffer = buffers.basePanel(parent, name)
|
self.buffer = buffers.twitter.basePanel(parent, name)
|
||||||
self.invisible = True
|
self.invisible = True
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = self.buffer.type
|
self.type = self.buffer.type
|
||||||
@ -471,6 +471,7 @@ class BaseBuffer(base.Buffer):
|
|||||||
|
|
||||||
def onFocus(self, *args, **kwargs):
|
def onFocus(self, *args, **kwargs):
|
||||||
tweet = self.get_tweet()
|
tweet = self.get_tweet()
|
||||||
|
if self.session.settings["general"]["relative_times"] == True:
|
||||||
# fix this:
|
# fix this:
|
||||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at, locale="en")
|
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())
|
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||||
@ -580,3 +581,83 @@ class BaseBuffer(base.Buffer):
|
|||||||
url = self.get_item_url()
|
url = self.get_item_url()
|
||||||
output.speak(_(u"Opening item in web browser..."))
|
output.speak(_(u"Opening item in web browser..."))
|
||||||
webbrowser.open(url)
|
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)
|
||||||
|
@ -6,12 +6,12 @@ import output
|
|||||||
import config
|
import config
|
||||||
import languageHandler
|
import languageHandler
|
||||||
import logging
|
import logging
|
||||||
from controller import messages
|
|
||||||
from sessions.twitter import compose, utils, templates
|
from sessions.twitter import compose, utils, templates
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
|
from controller.twitter import messages
|
||||||
from . import base
|
from . import base
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.dmBuffer")
|
log = logging.getLogger("controller.buffers.twitter.dmBuffer")
|
||||||
@ -69,7 +69,7 @@ class DirectMessagesBuffer(base.BaseBuffer):
|
|||||||
self.session.db["sent_direct_messages"] = sent_dms
|
self.session.db["sent_direct_messages"] = sent_dms
|
||||||
user_ids = [item.message_create["sender_id"] for item in items]
|
user_ids = [item.message_create["sender_id"] for item in items]
|
||||||
self.session.save_users(user_ids)
|
self.session.save_users(user_ids)
|
||||||
pub.sendMessage("more-sent-dms", data=sent, account=self.session.db["user_name"])
|
pub.sendMessage("twitter.more_sent_dms", data=sent, account=self.session.db["user_name"])
|
||||||
selected = self.buffer.list.get_selected()
|
selected = self.buffer.list.get_selected()
|
||||||
if self.session.settings["general"]["reverse_timelines"] == True:
|
if self.session.settings["general"]["reverse_timelines"] == True:
|
||||||
for i in received:
|
for i in received:
|
||||||
@ -162,3 +162,10 @@ class SentDirectMessagesBuffer(DirectMessagesBuffer):
|
|||||||
dm = self.get_right_tweet()
|
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"])
|
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
|
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,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from wxUI import dialogs, commonMessageDialogs
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import logging
|
import logging
|
||||||
from tweepy.cursor import Cursor
|
from tweepy.cursor import Cursor
|
||||||
|
from wxUI import dialogs, commonMessageDialogs
|
||||||
from . import base
|
from . import base
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.listBuffer")
|
log = logging.getLogger("controller.buffers.twitter.listBuffer")
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import time
|
import time
|
||||||
from wxUI import commonMessageDialogs, menus
|
|
||||||
from controller import user
|
|
||||||
from controller import messages
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import output
|
import output
|
||||||
@ -11,7 +8,9 @@ import logging
|
|||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
from controller.twitter import user, messages
|
||||||
from sessions.twitter import compose, templates
|
from sessions.twitter import compose, templates
|
||||||
|
from wxUI import commonMessageDialogs, menus
|
||||||
from . import base
|
from . import base
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.peopleBuffer")
|
log = logging.getLogger("controller.buffers.twitter.peopleBuffer")
|
||||||
@ -248,3 +247,8 @@ class PeopleBuffer(base.BaseBuffer):
|
|||||||
tweet = self.get_tweet()
|
tweet = self.get_tweet()
|
||||||
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
|
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
|
||||||
return url
|
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,10 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import time
|
import time
|
||||||
import locale
|
import locale
|
||||||
from wxUI import commonMessageDialogs
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import logging
|
import logging
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
|
from wxUI import commonMessageDialogs
|
||||||
from . import base, people
|
from . import base, people
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.searchBuffer")
|
log = logging.getLogger("controller.buffers.twitter.searchBuffer")
|
||||||
@ -62,6 +62,10 @@ class ConversationBuffer(SearchBuffer):
|
|||||||
last_thread_id = None
|
last_thread_id = None
|
||||||
last_reply_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):
|
def start_stream(self, start=False, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import time
|
import time
|
||||||
import wx
|
import wx
|
||||||
from wxUI import buffers, commonMessageDialogs, menus
|
|
||||||
from controller import user, messages
|
|
||||||
from controller import messages
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import output
|
import output
|
||||||
import logging
|
import logging
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
from wxUI import buffers, commonMessageDialogs, menus
|
||||||
|
from controller.twitter import user, messages
|
||||||
from controller.buffers import base
|
from controller.buffers import base
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.trends")
|
log = logging.getLogger("controller.buffers.twitter.trends")
|
||||||
@ -21,7 +20,7 @@ class TrendsBuffer(base.Buffer):
|
|||||||
self.session = sessionObject
|
self.session = sessionObject
|
||||||
self.account = account
|
self.account = account
|
||||||
self.invisible = True
|
self.invisible = True
|
||||||
self.buffer = buffers.trendsPanel(parent, name)
|
self.buffer = buffers.twitter.trendsPanel(parent, name)
|
||||||
self.buffer.account = account
|
self.buffer.account = account
|
||||||
self.type = self.buffer.type
|
self.type = self.buffer.type
|
||||||
self.bind_events()
|
self.bind_events()
|
||||||
|
File diff suppressed because it is too large
Load Diff
1
src/controller/mastodon/__init__.py
Normal file
1
src/controller/mastodon/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
179
src/controller/mastodon/handler.py
Normal file
179
src/controller/mastodon/handler.py
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
import logging
|
||||||
|
from pubsub import pub
|
||||||
|
from wxUI.dialogs.mastodon import dialogs
|
||||||
|
from wxUI.dialogs.mastodon import search as search_dialogs
|
||||||
|
from wxUI.dialogs.mastodon import dialogs
|
||||||
|
from wxUI import commonMessageDialogs
|
||||||
|
from sessions.twitter import utils
|
||||||
|
from . import userActions
|
||||||
|
|
||||||
|
log = logging.getLogger("controller.mastodon.handler")
|
||||||
|
|
||||||
|
class Handler(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(Handler, self).__init__()
|
||||||
|
|
||||||
|
def create_buffers(self, session, createAccounts=True, controller=None):
|
||||||
|
session.get_user_info()
|
||||||
|
name = session.get_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="timeline_home", name="home_timeline", sessionObject=session, account=name, sound="tweet_received.ogg"))
|
||||||
|
elif i == 'local':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Local"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="timeline_local", name="local_timeline", sessionObject=session, account=name, sound="tweet_received.ogg"))
|
||||||
|
elif i == 'federated':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Federated"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="timeline_public", name="federated_timeline", sessionObject=session, account=name, sound="tweet_received.ogg"))
|
||||||
|
elif i == 'mentions':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="MentionsBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="notifications", name="mentions", sessionObject=session, account=name, sound="mention_received.ogg"))
|
||||||
|
elif i == 'direct_messages':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="ConversationListBuffer", session_type=session.type, buffer_title=_("Direct messages"), parent_tab=root_position, start=False, kwargs=dict(compose_func="compose_conversation", parent=controller.view.nb, function="conversations", name="direct_messages", sessionObject=session, account=name, sound="dm_received.ogg"))
|
||||||
|
elif i == 'sent':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="account_statuses", name="sent", sessionObject=session, account=name, sound="tweet_received.ogg", id=session.db["user_id"]))
|
||||||
|
elif i == 'favorites':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Favorites"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="favourites", name="favorites", sessionObject=session, account=name, sound="favourite.ogg"))
|
||||||
|
elif i == 'bookmarks':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Bookmarks"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="bookmarks", name="bookmarks", sessionObject=session, account=name, sound="favourite.ogg"))
|
||||||
|
elif i == 'followers':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Followers"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_followers", name="followers", sessionObject=session, account=name, sound="update_followers.ogg", id=session.db["user_id"]))
|
||||||
|
elif i == 'following':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Following"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_following", name="following", sessionObject=session, account=name, sound="update_followers.ogg", id=session.db["user_id"]))
|
||||||
|
elif i == 'muted':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Muted users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="mutes", name="muted", sessionObject=session, account=name))
|
||||||
|
elif i == 'blocked':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="blocks", name="blocked", sessionObject=session, account=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=i, parent_tab=timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="account_statuses", name="%s-timeline".format(i), sessionObject=session, account=name, sound="tweet_timeline.ogg", id=i))
|
||||||
|
for i in session.settings["other_buffers"]["followers_timelines"]:
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Followers for {}").format(i), parent_tab=timelines_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_followers", name="%s-followers" % (i,), sessionObject=session, account=name, sound="new_event.ogg", id=i))
|
||||||
|
for i in session.settings["other_buffers"]["following_timelines"]:
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Following for {}").format(i), parent_tab=timelines_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_following", name="%s-following" % (i,), sessionObject=session, account=name, sound="new_event.ogg", 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", name))
|
||||||
|
# lists_position =controller.view.search("lists", session.db["user_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, 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", session.db["user_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, 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, name, trendsFor=i, sound="trends_updated.ogg"))
|
||||||
|
|
||||||
|
def start_buffer(self, controller, buffer):
|
||||||
|
if hasattr(buffer, "finished_timeline") and buffer.finished_timeline == False:
|
||||||
|
change_title = True
|
||||||
|
else:
|
||||||
|
change_title = False
|
||||||
|
try:
|
||||||
|
buffer.start_stream(play_sound=False)
|
||||||
|
except Exception 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))
|
||||||
|
if change_title:
|
||||||
|
pub.sendMessage("buffer-title-changed", buffer=buffer)
|
||||||
|
|
||||||
|
def open_conversation(self, controller, buffer):
|
||||||
|
post = buffer.get_item()
|
||||||
|
if post.reblog != None:
|
||||||
|
post = post.reblog
|
||||||
|
conversations_position =controller.view.search("direct_messages", buffer.session.get_name())
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="ConversationBuffer", session_type=buffer.session.type, buffer_title=_("Conversation with {0}").format(post.account.acct), parent_tab=conversations_position, start=True, kwargs=dict(parent=controller.view.nb, function="status_context", name="%s-conversation" % (post.id,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="search_updated.ogg", post=post, id=post.id))
|
||||||
|
|
||||||
|
def follow(self, buffer):
|
||||||
|
if not hasattr(buffer, "get_item"):
|
||||||
|
return
|
||||||
|
item = buffer.get_item()
|
||||||
|
if buffer.type == "user":
|
||||||
|
users = [item.acct]
|
||||||
|
elif buffer.type == "baseBuffer":
|
||||||
|
if item.reblog != None:
|
||||||
|
users = [user.acct for user in item.reblog.mentions if user.id != buffer.session.db["user_id"]]
|
||||||
|
if item.reblog.account.acct not in users and item.account.id != buffer.session.db["user_id"]:
|
||||||
|
users.insert(0, item.reblog.account.acct)
|
||||||
|
else:
|
||||||
|
users = [user.acct for user in item.mentions if user.id != buffer.session.db["user_id"]]
|
||||||
|
if item.account.acct not in users:
|
||||||
|
users.insert(0, item.account.acct)
|
||||||
|
u = userActions.userActions(buffer.session, users)
|
||||||
|
|
||||||
|
def search(self, controller, session, value):
|
||||||
|
log.debug("Creating a new search...")
|
||||||
|
dlg = search_dialogs.searchDialog(value)
|
||||||
|
if dlg.ShowModal() == wx.ID_OK and dlg.term.GetValue() != "":
|
||||||
|
term = dlg.term.GetValue()
|
||||||
|
searches_position =controller.view.search("searches", session.get_name())
|
||||||
|
if dlg.posts.GetValue() == True:
|
||||||
|
if term not in session.settings["other_buffers"]["post_searches"]:
|
||||||
|
session.settings["other_buffers"]["post_searches"].append(term)
|
||||||
|
session.settings.write()
|
||||||
|
# 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.users.GetValue() == True:
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_search", name="%s-searchUser" % (term,), sessionObject=session, account=session.get_name(), sound="search_updated.ogg", q=term))
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
if not hasattr(buffer, "get_item"):
|
||||||
|
return
|
||||||
|
item = buffer.get_item()
|
||||||
|
if buffer.type == "user":
|
||||||
|
users = [item.acct]
|
||||||
|
elif buffer.type == "baseBuffer":
|
||||||
|
if item.reblog != None:
|
||||||
|
users = [user.acct for user in item.reblog.mentions if user.id != buffer.session.db["user_id"]]
|
||||||
|
if item.reblog.account.acct not in users and item.account.id != buffer.session.db["user_id"]:
|
||||||
|
users.insert(0, item.reblog.account.acct)
|
||||||
|
else:
|
||||||
|
users = [user.acct for user in item.mentions if user.id != buffer.session.db["user_id"]]
|
||||||
|
if item.account.acct not in users and item.account.id != buffer.session.db["user_id"]:
|
||||||
|
users.insert(0, item.account.acct)
|
||||||
|
u = userActions.UserTimeline(buffer.session, users)
|
||||||
|
if u.dialog.ShowModal() == wx.ID_OK:
|
||||||
|
action = u.process_action()
|
||||||
|
if action == None:
|
||||||
|
return
|
||||||
|
user = u.user
|
||||||
|
if action == "posts":
|
||||||
|
if user.statuses_count == 0:
|
||||||
|
dialogs.no_posts()
|
||||||
|
return
|
||||||
|
if user.id 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(user.username,), parent_tab=timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="account_statuses", name="%s-timeline" % (user.id,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="tweet_timeline.ogg", id=user.id))
|
||||||
|
buffer.session.settings["other_buffers"]["timelines"].append(user.id)
|
||||||
|
buffer.session.sound.play("create_timeline.ogg")
|
||||||
|
elif action == "followers":
|
||||||
|
if user.followers_count == 0:
|
||||||
|
dialogs.no_followers()
|
||||||
|
return
|
||||||
|
if user.id in buffer.session.settings["other_buffers"]["followers_timelines"]:
|
||||||
|
commonMessageDialogs.timeline_exist()
|
||||||
|
return
|
||||||
|
timelines_position =controller.view.search("timelines", buffer.session.get_name())
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=buffer.session.type, buffer_title=_("Followers for {}").format(user.username,), parent_tab=timelines_position, start=True, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_followers", name="%s-followers" % (user.id,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="new_event.ogg", id=user.id))
|
||||||
|
buffer.session.settings["other_buffers"]["followers_timelines"].append(user.id)
|
||||||
|
buffer.session.sound.play("create_timeline.ogg")
|
||||||
|
elif action == "following":
|
||||||
|
if user.following_count == 0:
|
||||||
|
dialogs.no_following()
|
||||||
|
return
|
||||||
|
if user.id in buffer.session.settings["other_buffers"]["following_timelines"]:
|
||||||
|
commonMessageDialogs.timeline_exist()
|
||||||
|
return
|
||||||
|
timelines_position =controller.view.search("timelines", buffer.session.get_name())
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=buffer.session.type, buffer_title=_("Following for {}").format(user.username,), parent_tab=timelines_position, start=True, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="account_following", name="%s-followers" % (user.id,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="new_event.ogg", id=user.id))
|
||||||
|
buffer.session.settings["other_buffers"]["following_timelines"].append(user.id)
|
||||||
|
buffer.session.sound.play("create_timeline.ogg")
|
||||||
|
buffer.session.settings.write()
|
227
src/controller/mastodon/messages.py
Normal file
227
src/controller/mastodon/messages.py
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
import config
|
||||||
|
import output
|
||||||
|
from controller.twitter import messages
|
||||||
|
from sessions.mastodon import templates
|
||||||
|
from wxUI.dialogs.mastodon import postDialogs
|
||||||
|
|
||||||
|
class post(messages.basicTweet):
|
||||||
|
def __init__(self, session, title, caption, text="", *args, **kwargs):
|
||||||
|
# take max character limit from session as this might be different for some instances.
|
||||||
|
self.max = session.char_limit
|
||||||
|
self.title = title
|
||||||
|
self.session = session
|
||||||
|
self.message = postDialogs.Post(caption=caption, text=text, *args, **kwargs)
|
||||||
|
self.message.SetTitle(title)
|
||||||
|
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.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||||
|
widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||||
|
widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach)
|
||||||
|
widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
|
||||||
|
# ToDo: Add autocomplete feature to mastodon and uncomment this.
|
||||||
|
# widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||||
|
widgetUtils.connect_event(self.message.add_post, widgetUtils.BUTTON_PRESSED, self.add_post)
|
||||||
|
widgetUtils.connect_event(self.message.remove_post, widgetUtils.BUTTON_PRESSED, self.remove_post)
|
||||||
|
self.attachments = []
|
||||||
|
self.thread = []
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
|
def add_post(self, event, update_gui=True, *args, **kwargs):
|
||||||
|
text = self.message.text.GetValue()
|
||||||
|
attachments = self.attachments[::]
|
||||||
|
postdata = dict(text=text, attachments=attachments, sensitive=self.message.sensitive.GetValue(), spoiler_text=None)
|
||||||
|
if postdata.get("sensitive") == True:
|
||||||
|
postdata.update(spoiler_text=self.message.spoiler.GetValue())
|
||||||
|
self.thread.append(postdata)
|
||||||
|
self.attachments = []
|
||||||
|
if update_gui:
|
||||||
|
self.message.reset_controls()
|
||||||
|
self.message.add_item(item=[text, len(attachments)], list_type="post")
|
||||||
|
self.message.text.SetFocus()
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
|
def get_post_data(self):
|
||||||
|
self.add_post(event=None, update_gui=False)
|
||||||
|
return self.thread
|
||||||
|
|
||||||
|
def text_processor(self, *args, **kwargs):
|
||||||
|
super(post, self).text_processor(*args, **kwargs)
|
||||||
|
if len(self.thread) > 0:
|
||||||
|
if hasattr(self.message, "posts"):
|
||||||
|
self.message.posts.Enable(True)
|
||||||
|
self.message.remove_post.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.posts.Enable(False)
|
||||||
|
self.message.remove_post.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 len(self.message.text.GetValue()) > 0 or len(self.attachments) > 0:
|
||||||
|
self.message.add_post.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.add_post.Enable(False)
|
||||||
|
|
||||||
|
def remove_post(self, *args, **kwargs):
|
||||||
|
post = self.message.posts.GetFocusedItem()
|
||||||
|
if post > -1 and len(self.thread) > post:
|
||||||
|
self.thread.pop(post)
|
||||||
|
self.message.remove_item(list_type="post")
|
||||||
|
self.text_processor()
|
||||||
|
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"] == "poll" or self.attachments[0]["type"] == "video" or self.attachments[0]["type"] == "audio"):
|
||||||
|
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)
|
||||||
|
self.message.Bind(wx.EVT_MENU, self.on_attach_audio, self.message.add_audio)
|
||||||
|
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()
|
||||||
|
big_media_present = False
|
||||||
|
for a in self.attachments:
|
||||||
|
if a["type"] == "video" or a["type"] == "audio" or a["type"] == "poll":
|
||||||
|
big_media_present = True
|
||||||
|
break
|
||||||
|
if can_attach == False or big_media_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) >= 4:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
can_attach = self.can_attach()
|
||||||
|
big_media_present = False
|
||||||
|
for a in self.attachments:
|
||||||
|
if a["type"] == "video" or a["type"] == "audio" or a["type"] == "poll":
|
||||||
|
big_media_present = True
|
||||||
|
break
|
||||||
|
if can_attach == False or big_media_present == True:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
video = self.message.get_video()
|
||||||
|
if video != None:
|
||||||
|
videoInfo = {"type": "video", "file": video, "description": ""}
|
||||||
|
self.attachments.append(videoInfo)
|
||||||
|
self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]])
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
|
def on_attach_audio(self, *args, **kwargs):
|
||||||
|
if len(self.attachments) >= 4:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
can_attach = self.can_attach()
|
||||||
|
big_media_present = False
|
||||||
|
for a in self.attachments:
|
||||||
|
if a["type"] == "video" or a["type"] == "audio" or a["type"] == "poll":
|
||||||
|
big_media_present = True
|
||||||
|
break
|
||||||
|
if can_attach == False or big_media_present == True:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
audio = self.message.get_audio()
|
||||||
|
if audio != None:
|
||||||
|
audioInfo = {"type": "audio", "file": audio, "description": ""}
|
||||||
|
self.attachments.append(audioInfo)
|
||||||
|
self.message.add_item(item=[os.path.basename(audioInfo["file"]), audioInfo["type"], audioInfo["description"]])
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
|
def on_attach_poll(self, *args, **kwargs):
|
||||||
|
if len(self.attachments) > 0:
|
||||||
|
return self.message.unable_to_attach_poll()
|
||||||
|
can_attach = self.can_attach()
|
||||||
|
big_media_present = False
|
||||||
|
for a in self.attachments:
|
||||||
|
if a["type"] == "video" or a["type"] == "audio" or a["type"] == "poll":
|
||||||
|
big_media_present = True
|
||||||
|
break
|
||||||
|
if can_attach == False or big_media_present == True:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
dlg = postDialogs.poll()
|
||||||
|
if dlg.ShowModal() == wx.ID_OK:
|
||||||
|
day = 86400
|
||||||
|
periods = [300, 1800, 3600, 21600, day, day*2, day*3, day*4, day*5, day*6, day*7]
|
||||||
|
period = periods[dlg.period.GetSelection()]
|
||||||
|
poll_options = dlg.get_options()
|
||||||
|
multiple = dlg.multiple.GetValue()
|
||||||
|
hide_totals = dlg.hide_votes.GetValue()
|
||||||
|
data = dict(type="poll", file="", description=_("Poll with {} options").format(len(poll_options)), options=poll_options, expires_in=period, multiple=multiple, hide_totals=hide_totals)
|
||||||
|
self.attachments.append(data)
|
||||||
|
self.message.add_item(item=[data["file"], data["type"], data["description"]])
|
||||||
|
self.text_processor()
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
self.add_post(event=None, update_gui=False)
|
||||||
|
return self.thread
|
||||||
|
|
||||||
|
def get_visibility(self):
|
||||||
|
visibility_settings = ["public", "unlisted", "private", "direct"]
|
||||||
|
return visibility_settings[self.message.visibility.GetSelection()]
|
||||||
|
|
||||||
|
class viewPost(post):
|
||||||
|
def __init__(self, post, offset_hours=0, date="", item_url=""):
|
||||||
|
if post.reblog != None:
|
||||||
|
post = post.reblog
|
||||||
|
author = post.account.display_name if post.account.display_name != "" else post.account.username
|
||||||
|
title = _(u"Post from {}").format(author)
|
||||||
|
image_description = templates.process_image_descriptions(post.media_attachments)
|
||||||
|
text = templates.process_text(post, safe=False)
|
||||||
|
date = templates.process_date(post.created_at, relative_times=False, offset_hours=offset_hours)
|
||||||
|
privacy_settings = dict(public=_("Public"), unlisted=_("Not listed"), private=_("followers only"), direct=_("Direct"))
|
||||||
|
privacy = privacy_settings.get(post.visibility)
|
||||||
|
boost_count = str(post.reblogs_count)
|
||||||
|
favs_count = str(post.favourites_count)
|
||||||
|
# Gets the client from where this post was made.
|
||||||
|
source_obj = post.get("application")
|
||||||
|
if source_obj == None:
|
||||||
|
source = _("Remote instance")
|
||||||
|
else:
|
||||||
|
source = source_obj.get("name")
|
||||||
|
self.message = postDialogs.viewPost(text=text, boosts_count=boost_count, favs_count=favs_count, source=source, date=date, privacy=privacy)
|
||||||
|
self.message.SetTitle(title)
|
||||||
|
if image_description != "":
|
||||||
|
self.message.image_description.Enable(True)
|
||||||
|
self.message.image_description.ChangeValue(image_description)
|
||||||
|
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 share(self, *args, **kwargs):
|
||||||
|
if hasattr(self, "item_url"):
|
||||||
|
output.copy(self.item_url)
|
||||||
|
output.speak(_("Link copied to clipboard."))
|
101
src/controller/mastodon/userActions.py
Normal file
101
src/controller/mastodon/userActions.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
import widgetUtils
|
||||||
|
import output
|
||||||
|
from wxUI.dialogs.mastodon import userActions as userActionsDialog
|
||||||
|
from wxUI.dialogs.mastodon import userTimeline as userTimelineDialog
|
||||||
|
from pubsub import pub
|
||||||
|
from mastodon import MastodonError, MastodonNotFoundError
|
||||||
|
from extra.autocompletionUsers import completion
|
||||||
|
|
||||||
|
log = logging.getLogger("controller.mastodon.userActions")
|
||||||
|
|
||||||
|
class BasicUserSelector(object):
|
||||||
|
def __init__(self, session, users=[]):
|
||||||
|
super(BasicUserSelector, self).__init__()
|
||||||
|
self.session = session
|
||||||
|
self.create_dialog(users=users)
|
||||||
|
|
||||||
|
def create_dialog(self, users):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def autocomplete_users(self, *args, **kwargs):
|
||||||
|
c = completion.autocompletionUsers(self.dialog, self.session.session_id)
|
||||||
|
c.show_menu("dm")
|
||||||
|
|
||||||
|
def search_user(self, user):
|
||||||
|
try:
|
||||||
|
user = self.session.api.account_lookup(user)
|
||||||
|
return user
|
||||||
|
except MastodonError:
|
||||||
|
log.exception("Error searching for user %s.".format(user))
|
||||||
|
|
||||||
|
class userActions(BasicUserSelector):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(userActions, self).__init__(*args, **kwargs)
|
||||||
|
if self.dialog.get_response() == widgetUtils.OK:
|
||||||
|
self.process_action()
|
||||||
|
|
||||||
|
def create_dialog(self, users):
|
||||||
|
self.dialog = userActionsDialog.UserActionsDialog(users)
|
||||||
|
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||||
|
|
||||||
|
def process_action(self):
|
||||||
|
action = self.dialog.get_action()
|
||||||
|
user = self.dialog.get_user()
|
||||||
|
user = self.search_user(user)
|
||||||
|
if user == None:
|
||||||
|
return
|
||||||
|
getattr(self, action)(user)
|
||||||
|
|
||||||
|
def follow(self, user):
|
||||||
|
try:
|
||||||
|
self.session.api.account_follow(user.id)
|
||||||
|
except MastodonError as err:
|
||||||
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
|
def unfollow(self, user):
|
||||||
|
try:
|
||||||
|
result = self.session.api.account_unfollow(user.id)
|
||||||
|
except MastodonError as err:
|
||||||
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
|
def mute(self, user):
|
||||||
|
try:
|
||||||
|
id = self.session.api.account_mute(user.id)
|
||||||
|
except MastodonError as err:
|
||||||
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
|
def unmute(self, user):
|
||||||
|
try:
|
||||||
|
id = self.session.api.account_unmute(user.id)
|
||||||
|
except MastodonError as err:
|
||||||
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
|
def block(self, user):
|
||||||
|
try:
|
||||||
|
id = self.session.api.account_block(user.id)
|
||||||
|
except MastodonError as err:
|
||||||
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
|
def unblock(self, user):
|
||||||
|
try:
|
||||||
|
id = self.session.api.account_unblock(user.id)
|
||||||
|
except MastodonError as err:
|
||||||
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
|
class UserTimeline(BasicUserSelector):
|
||||||
|
|
||||||
|
def create_dialog(self, users):
|
||||||
|
self.dialog = userTimelineDialog.UserTimeline(users)
|
||||||
|
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||||
|
|
||||||
|
def process_action(self):
|
||||||
|
action = self.dialog.get_action()
|
||||||
|
user = self.dialog.get_user()
|
||||||
|
user = self.search_user(user)
|
||||||
|
if user == None:
|
||||||
|
return
|
||||||
|
self.user = user
|
||||||
|
return action
|
@ -1,25 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
|
||||||
import threading
|
|
||||||
import logging
|
import logging
|
||||||
import sound_lib
|
|
||||||
import paths
|
import paths
|
||||||
import widgetUtils
|
|
||||||
import config
|
import config
|
||||||
import languageHandler
|
import languageHandler
|
||||||
import output
|
|
||||||
import application
|
import application
|
||||||
import config_utils
|
|
||||||
import keys
|
|
||||||
from collections import OrderedDict
|
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from mysc import autostart as autostart_windows
|
from mysc import autostart as autostart_windows
|
||||||
from wxUI.dialogs import configuration
|
from wxUI.dialogs import configuration
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from extra.autocompletionUsers import scan, manage
|
|
||||||
from extra.ocr import OCRSpace
|
|
||||||
from .editTemplateController import EditTemplate
|
|
||||||
|
|
||||||
log = logging.getLogger("Settings")
|
log = logging.getLogger("Settings")
|
||||||
|
|
||||||
@ -132,234 +121,3 @@ class globalSettingsController(object):
|
|||||||
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
|
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
|
||||||
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
|
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
|
||||||
config.app.write()
|
config.app.write()
|
||||||
|
|
||||||
class accountSettingsController(globalSettingsController):
|
|
||||||
def __init__(self, buffer, window):
|
|
||||||
self.user = buffer.session.db["user_name"]
|
|
||||||
self.buffer = buffer
|
|
||||||
self.window = window
|
|
||||||
self.config = buffer.session.settings
|
|
||||||
super(accountSettingsController, self).__init__()
|
|
||||||
|
|
||||||
def 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", 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
src/controller/twitter/__init__.py
Normal file
1
src/controller/twitter/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
340
src/controller/twitter/handler.py
Normal file
340
src/controller/twitter/handler.py
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
# -*- 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__()
|
||||||
|
|
||||||
|
def create_buffers(self, session, createAccounts=True, controller=None):
|
||||||
|
session.get_user_info()
|
||||||
|
name = session.get_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()
|
@ -10,9 +10,10 @@ from pubsub import pub
|
|||||||
log = logging.getLogger("controller.listsController")
|
log = logging.getLogger("controller.listsController")
|
||||||
|
|
||||||
class listsController(object):
|
class listsController(object):
|
||||||
def __init__(self, session, user=None):
|
def __init__(self, session, user=None, lists_buffer_position=0):
|
||||||
super(listsController, self).__init__()
|
super(listsController, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
|
self.lists_buffer_position = lists_buffer_position
|
||||||
if user == None:
|
if user == None:
|
||||||
self.dialog = lists.listViewer()
|
self.dialog = lists.listViewer()
|
||||||
self.dialog.populate_list(self.get_all_lists())
|
self.dialog.populate_list(self.get_all_lists())
|
||||||
@ -90,7 +91,9 @@ class listsController(object):
|
|||||||
def open_list_as_buffer(self, *args, **kwargs):
|
def open_list_as_buffer(self, *args, **kwargs):
|
||||||
if self.dialog.lista.get_count() == 0: return
|
if self.dialog.lista.get_count() == 0: return
|
||||||
list = self.session.db["lists"][self.dialog.get_item()]
|
list = self.session.db["lists"][self.dialog.get_item()]
|
||||||
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list.name)
|
pub.sendMessage("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):
|
def subscribe(self, *args, **kwargs):
|
||||||
if self.dialog.lista.get_count() == 0: return
|
if self.dialog.lista.get_count() == 0: return
|
@ -8,7 +8,7 @@ import output
|
|||||||
import sound
|
import sound
|
||||||
import config
|
import config
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from twitter_text import parse_tweet
|
from twitter_text.parse_tweet import parse_tweet
|
||||||
from wxUI.dialogs import twitterDialogs, urlList
|
from wxUI.dialogs import twitterDialogs, urlList
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from extra import translator, SpellChecker
|
from extra import translator, SpellChecker
|
||||||
@ -23,7 +23,7 @@ class basicTweet(object):
|
|||||||
self.max = max
|
self.max = max
|
||||||
self.title = title
|
self.title = title
|
||||||
self.session = session
|
self.session = session
|
||||||
self.message = getattr(twitterDialogs, messageType)(title=title, caption=caption, message=text, *args, **kwargs)
|
self.message = getattr(twitterDialogs, messageType)(title=title, caption=caption, message=text, max_length=max, *args, **kwargs)
|
||||||
self.message.text.SetValue(text)
|
self.message.text.SetValue(text)
|
||||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
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.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||||
@ -185,6 +185,7 @@ class tweet(basicTweet):
|
|||||||
def text_processor(self, *args, **kwargs):
|
def text_processor(self, *args, **kwargs):
|
||||||
super(tweet, self).text_processor(*args, **kwargs)
|
super(tweet, self).text_processor(*args, **kwargs)
|
||||||
if len(self.thread) > 0:
|
if len(self.thread) > 0:
|
||||||
|
if hasattr(self.message, "tweets"):
|
||||||
self.message.tweets.Enable(True)
|
self.message.tweets.Enable(True)
|
||||||
self.message.remove_tweet.Enable(True)
|
self.message.remove_tweet.Enable(True)
|
||||||
else:
|
else:
|
247
src/controller/twitter/settings.py
Normal file
247
src/controller/twitter/settings.py
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
# -*- 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()
|
@ -29,42 +29,42 @@ class userActionsController(object):
|
|||||||
def follow(self, user):
|
def follow(self, user):
|
||||||
try:
|
try:
|
||||||
self.session.twitter.create_friendship(screen_name=user )
|
self.session.twitter.create_friendship(screen_name=user )
|
||||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
||||||
except TweepyException as err:
|
except TweepyException as err:
|
||||||
output.speak("Error %s" % (str(err)), True)
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
def unfollow(self, user):
|
def unfollow(self, user):
|
||||||
try:
|
try:
|
||||||
id = self.session.twitter.destroy_friendship(screen_name=user )
|
id = self.session.twitter.destroy_friendship(screen_name=user )
|
||||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
||||||
except TweepyException as err:
|
except TweepyException as err:
|
||||||
output.speak("Error %s" % (str(err)), True)
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
def mute(self, user):
|
def mute(self, user):
|
||||||
try:
|
try:
|
||||||
id = self.session.twitter.create_mute(screen_name=user )
|
id = self.session.twitter.create_mute(screen_name=user )
|
||||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
||||||
except TweepyException as err:
|
except TweepyException as err:
|
||||||
output.speak("Error %s" % (str(err)), True)
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
def unmute(self, user):
|
def unmute(self, user):
|
||||||
try:
|
try:
|
||||||
id = self.session.twitter.destroy_mute(screen_name=user )
|
id = self.session.twitter.destroy_mute(screen_name=user )
|
||||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
||||||
except TweepyException as err:
|
except TweepyException as err:
|
||||||
output.speak("Error %s" % (str(err)), True)
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
def report(self, user):
|
def report(self, user):
|
||||||
try:
|
try:
|
||||||
id = self.session.twitter.report_spam(screen_name=user )
|
id = self.session.twitter.report_spam(screen_name=user )
|
||||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
||||||
except TweepyException as err:
|
except TweepyException as err:
|
||||||
output.speak("Error %s" % (str(err)), True)
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
||||||
def block(self, user):
|
def block(self, user):
|
||||||
try:
|
try:
|
||||||
id = self.session.twitter.create_block(screen_name=user )
|
id = self.session.twitter.create_block(screen_name=user )
|
||||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
|
||||||
except TweepyException as err:
|
except TweepyException as err:
|
||||||
output.speak("Error %s" % (str(err)), True)
|
output.speak("Error %s" % (str(err)), True)
|
||||||
|
|
@ -21,7 +21,7 @@ def parse(s):
|
|||||||
lst.remove(item)
|
lst.remove(item)
|
||||||
#end if
|
#end if
|
||||||
if len(lst) > 1: #more than one key, parse error
|
if len(lst) > 1: #more than one key, parse error
|
||||||
raise ValueError, 'unknown modifier %s' % lst[0]
|
raise ValueError('unknown modifier %s' % lst[0])
|
||||||
return (m, lst[0].lower())
|
return (m, lst[0].lower())
|
||||||
class AtspiThread(threading.Thread):
|
class AtspiThread(threading.Thread):
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -6,27 +6,26 @@ from wxUI.dialogs import baseDialog
|
|||||||
class keystrokeEditorDialog(baseDialog.BaseWXDialog):
|
class keystrokeEditorDialog(baseDialog.BaseWXDialog):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(keystrokeEditorDialog, self).__init__(parent=None, id=-1, title=_(u"Keystroke editor"))
|
super(keystrokeEditorDialog, self).__init__(parent=None, id=-1, title=_(u"Keystroke editor"))
|
||||||
panel = wx.Panel(self)
|
|
||||||
self.actions = []
|
self.actions = []
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
keysText = wx.StaticText(panel, -1, _(u"Select a keystroke to edit"))
|
keysText = wx.StaticText(self, -1, _(u"Select a keystroke to edit"))
|
||||||
self.keys = widgets.list(self, _(u"Action"), _(u"Keystroke"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(400, 450))
|
self.keys = widgets.list(self, _(u"Action"), _(u"Keystroke"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(400, 450))
|
||||||
self.keys.list.SetFocus()
|
self.keys.list.SetFocus()
|
||||||
firstSizer = wx.BoxSizer(wx.HORIZONTAL)
|
firstSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
firstSizer.Add(keysText, 0, wx.ALL, 5)
|
firstSizer.Add(keysText, 0, wx.ALL, 5)
|
||||||
firstSizer.Add(self.keys.list, 0, wx.ALL, 5)
|
firstSizer.Add(self.keys.list, 0, wx.ALL, 5)
|
||||||
self.edit = wx.Button(panel, -1, _(u"Edit"))
|
self.edit = wx.Button(self, -1, _(u"Edit"))
|
||||||
self.edit.SetDefault()
|
self.edit.SetDefault()
|
||||||
self.undefine = wx.Button(panel, -1, _("Undefine keystroke"))
|
self.undefine = wx.Button(self, -1, _("Undefine keystroke"))
|
||||||
self.execute = wx.Button(panel, -1, _(u"Execute action"))
|
self.execute = wx.Button(self, -1, _(u"Execute action"))
|
||||||
close = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
|
close = wx.Button(self, wx.ID_CANCEL, _(u"Close"))
|
||||||
secondSizer = wx.BoxSizer(wx.HORIZONTAL)
|
secondSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
secondSizer.Add(self.edit, 0, wx.ALL, 5)
|
secondSizer.Add(self.edit, 0, wx.ALL, 5)
|
||||||
secondSizer.Add(self.execute, 0, wx.ALL, 5)
|
secondSizer.Add(self.execute, 0, wx.ALL, 5)
|
||||||
secondSizer.Add(close, 0, wx.ALL, 5)
|
secondSizer.Add(close, 0, wx.ALL, 5)
|
||||||
sizer.Add(firstSizer, 0, wx.ALL, 5)
|
sizer.Add(firstSizer, 0, wx.ALL, 5)
|
||||||
sizer.Add(secondSizer, 0, wx.ALL, 5)
|
sizer.Add(secondSizer, 0, wx.ALL, 5)
|
||||||
panel.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
self.SetClientSize(sizer.CalcMin())
|
self.SetClientSize(sizer.CalcMin())
|
||||||
|
|
||||||
def put_keystrokes(self, actions, keystrokes):
|
def put_keystrokes(self, actions, keystrokes):
|
||||||
|
53
src/mastodon.defaults
Normal file
53
src/mastodon.defaults
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
[mastodon]
|
||||||
|
access_token = string(default="")
|
||||||
|
instance = string(default="")
|
||||||
|
user_name = string(default="")
|
||||||
|
ignored_clients = list(default=list())
|
||||||
|
|
||||||
|
[general]
|
||||||
|
relative_times = boolean(default=True)
|
||||||
|
max_posts_per_call = integer(default=40)
|
||||||
|
reverse_timelines = boolean(default=False)
|
||||||
|
persist_size = integer(default=0)
|
||||||
|
load_cache_in_memory=boolean(default=True)
|
||||||
|
show_screen_names = boolean(default=False)
|
||||||
|
buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications'))
|
||||||
|
boost_mode = string(default="ask")
|
||||||
|
|
||||||
|
[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_img = boolean(default=True)
|
||||||
|
|
||||||
|
[other_buffers]
|
||||||
|
timelines = list(default=list())
|
||||||
|
searches = list(default=list())
|
||||||
|
lists = list(default=list())
|
||||||
|
followers_timelines = list(default=list())
|
||||||
|
following_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]
|
||||||
|
post = string(default="$display_name, $safe_text $image_descriptions $date. $visibility. $source")
|
||||||
|
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
|
||||||
|
conversation = string(default="Conversation with $users. Last message: $last_post")
|
||||||
|
|
||||||
|
[filters]
|
||||||
|
|
||||||
|
[user-aliases]
|
@ -1,18 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
testmodules = ["test.test_cache"]
|
|
||||||
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
|
|
||||||
for t in testmodules:
|
|
||||||
try:
|
|
||||||
# If the module defines a suite() function, call it to get the suite.
|
|
||||||
mod = __import__(t, globals(), locals(), ['suite'])
|
|
||||||
suitefn = getattr(mod, 'suite')
|
|
||||||
suite.addTest(suitefn())
|
|
||||||
except (ImportError, AttributeError):
|
|
||||||
# else, just load all the test cases from the module.
|
|
||||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))
|
|
||||||
|
|
||||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
|
@ -1,42 +1,25 @@
|
|||||||
# -*- coding: cp1252 -*-
|
# -*- coding: cp1252 -*-
|
||||||
#from config_utils import Configuration, ConfigurationResetException
|
""" Lightweigth module that saves session position across global config and performs validation of config files. """
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
import config
|
|
||||||
import paths
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import config
|
||||||
|
import paths
|
||||||
log = logging.getLogger("sessionmanager.manager")
|
log = logging.getLogger("sessionmanager.manager")
|
||||||
from sessions import session_exceptions
|
from sessions import session_exceptions
|
||||||
|
|
||||||
manager = None
|
manager = None
|
||||||
def setup():
|
def setup():
|
||||||
|
""" Creates the singleton instance used within TWBlue to access this object. """
|
||||||
global manager
|
global manager
|
||||||
if not manager:
|
if not manager:
|
||||||
manager = sessionManager()
|
manager = sessionManager()
|
||||||
|
|
||||||
class sessionManager(object):
|
class sessionManager(object):
|
||||||
# def __init__(self):
|
|
||||||
# FILE = "sessions.conf"
|
|
||||||
# SPEC = "app-configuration.defaults"
|
|
||||||
# try:
|
|
||||||
# self.main = Configuration(paths.config_path(FILE), paths.app_path(SPEC))
|
|
||||||
# except ConfigurationResetException:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
def get_current_session(self):
|
def get_current_session(self):
|
||||||
|
""" Returns the currently focused session, if valid. """
|
||||||
if self.is_valid(config.app["sessions"]["current_session"]):
|
if self.is_valid(config.app["sessions"]["current_session"]):
|
||||||
return config.app["sessions"]["current_session"]
|
return config.app["sessions"]["current_session"]
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def add_session(self, id):
|
|
||||||
log.debug("Adding a new session: %s" % (id,))
|
|
||||||
path = os.path.join(paths.config_path(), id)
|
|
||||||
if not os.path.exists(path):
|
|
||||||
log.debug("Creating %s path" % (os.path.join(paths.config_path(), path),))
|
|
||||||
os.mkdir(path)
|
|
||||||
config.app["sessions"]["sessions"].append(id)
|
|
||||||
|
|
||||||
def set_current_session(self, sessionID):
|
def set_current_session(self, sessionID):
|
||||||
config.app["sessions"]["current_session"] = sessionID
|
config.app["sessions"]["current_session"] = sessionID
|
||||||
|
@ -1,38 +1,51 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import shutil
|
""" Module to perform session actions such as addition, removal or display of the global settings dialogue. """
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from . import wxUI as view
|
|
||||||
from controller import settings
|
|
||||||
import paths
|
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import widgetUtils
|
||||||
import sessions
|
import sessions
|
||||||
from sessions.twitter import session
|
import output
|
||||||
from . import manager
|
import paths
|
||||||
import config_utils
|
import config_utils
|
||||||
import config
|
import config
|
||||||
|
from pubsub import pub
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
|
from controller import settings
|
||||||
|
from sessions.twitter import session as TwitterSession
|
||||||
|
from sessions.mastodon import session as MastodonSession
|
||||||
|
from . import manager
|
||||||
|
from . import wxUI as view
|
||||||
|
|
||||||
log = logging.getLogger("sessionmanager.sessionManager")
|
log = logging.getLogger("sessionmanager.sessionManager")
|
||||||
|
|
||||||
class sessionManagerController(object):
|
class sessionManagerController(object):
|
||||||
def __init__(self, started=False):
|
def __init__(self, started: bool = False):
|
||||||
|
""" Class constructor.
|
||||||
|
|
||||||
|
Creates the SessionManager class controller, responsible for the accounts within TWBlue. From this dialog, users can add/Remove accounts, or load the global settings dialog.
|
||||||
|
|
||||||
|
:param started: Indicates whether this object is being created during application startup (when no other controller has been instantiated) or not. It is important for us to know this, as we won't show the button to open global settings dialog if the application has been started. Users must choose the corresponding option in the menu bar.
|
||||||
|
:type started: bool
|
||||||
|
"""
|
||||||
super(sessionManagerController, self).__init__()
|
super(sessionManagerController, self).__init__()
|
||||||
log.debug("Setting up the session manager.")
|
log.debug("Setting up the session manager.")
|
||||||
self.started = started
|
self.started = started
|
||||||
|
# Initialize the manager, responsible for storing session objects.
|
||||||
manager.setup()
|
manager.setup()
|
||||||
self.view = view.sessionManagerWindow()
|
self.view = view.sessionManagerWindow()
|
||||||
widgetUtils.connect_event(self.view.new, widgetUtils.BUTTON_PRESSED, self.manage_new_account)
|
pub.subscribe(self.manage_new_account, "sessionmanager.new_account")
|
||||||
widgetUtils.connect_event(self.view.remove, widgetUtils.BUTTON_PRESSED, self.remove)
|
pub.subscribe(self.remove_account, "sessionmanager.remove_account")
|
||||||
if self.started == False:
|
if self.started == False:
|
||||||
widgetUtils.connect_event(self.view.configuration, widgetUtils.BUTTON_PRESSED, self.configuration)
|
pub.subscribe(self.configuration, "sessionmanager.configuration")
|
||||||
else:
|
else:
|
||||||
self.view.hide_configuration()
|
self.view.hide_configuration()
|
||||||
|
# Store a temporary copy of new and removed sessions, so we will perform actions on them during call to on_ok.
|
||||||
self.new_sessions = {}
|
self.new_sessions = {}
|
||||||
self.removed_sessions = []
|
self.removed_sessions = []
|
||||||
|
|
||||||
def fill_list(self):
|
def fill_list(self):
|
||||||
|
""" Fills the session list with all valid sessions that could be found in config path. """
|
||||||
sessionsList = []
|
sessionsList = []
|
||||||
reserved_dirs = ["dicts"]
|
reserved_dirs = ["dicts"]
|
||||||
log.debug("Filling the sessions list.")
|
log.debug("Filling the sessions list.")
|
||||||
@ -51,10 +64,16 @@ 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
|
||||||
name = config_test["twitter"]["user_name"]
|
if config_test.get("twitter") != None:
|
||||||
|
name = _("{account_name} (Twitter)").format(account_name=config_test["twitter"]["user_name"])
|
||||||
if config_test["twitter"]["user_key"] != "" and config_test["twitter"]["user_secret"] != "":
|
if config_test["twitter"]["user_key"] != "" and config_test["twitter"]["user_secret"] != "":
|
||||||
sessionsList.append(name)
|
sessionsList.append(name)
|
||||||
self.sessions.append(i)
|
self.sessions.append(dict(type="twitter", id=i))
|
||||||
|
elif config_test.get("mastodon") != None:
|
||||||
|
name = _("{account_name} (Mastodon)").format(account_name=config_test["mastodon"]["user_name"])
|
||||||
|
if config_test["mastodon"]["instance"] != "" and config_test["mastodon"]["access_token"] != "":
|
||||||
|
sessionsList.append(name)
|
||||||
|
self.sessions.append(dict(type="mastodon", id=i))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
log.debug("Deleting session %s" % (i,))
|
log.debug("Deleting session %s" % (i,))
|
||||||
@ -65,6 +84,7 @@ class sessionManagerController(object):
|
|||||||
self.view.fill_list(sessionsList)
|
self.view.fill_list(sessionsList)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
|
""" Displays the session manager dialog. """
|
||||||
if self.view.get_response() == widgetUtils.OK:
|
if self.view.get_response() == widgetUtils.OK:
|
||||||
self.do_ok()
|
self.do_ok()
|
||||||
# else:
|
# else:
|
||||||
@ -73,49 +93,49 @@ class sessionManagerController(object):
|
|||||||
def do_ok(self):
|
def do_ok(self):
|
||||||
log.debug("Starting sessions...")
|
log.debug("Starting sessions...")
|
||||||
for i in self.sessions:
|
for i in self.sessions:
|
||||||
if (i in sessions.sessions) == True: continue
|
# Skip already created sessions. Useful when session manager controller is not created during startup.
|
||||||
s = session.Session(i)
|
if sessions.sessions.get(i.get("id")) != None:
|
||||||
|
continue
|
||||||
|
# Create the session object based in session type.
|
||||||
|
if i.get("type") == "twitter":
|
||||||
|
s = TwitterSession.Session(i.get("id"))
|
||||||
|
elif i.get("type") == "mastodon":
|
||||||
|
s = MastodonSession.Session(i.get("id"))
|
||||||
s.get_configuration()
|
s.get_configuration()
|
||||||
if i 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 TweepyException:
|
||||||
self.show_auth_error(s.settings["twitter"]["user_name"])
|
self.show_auth_error(s.settings["twitter"]["user_name"])
|
||||||
continue
|
continue
|
||||||
sessions.sessions[i] = s
|
sessions.sessions[i.get("id")] = s
|
||||||
self.new_sessions[i] = 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, user_name):
|
||||||
error = view.auth_error(user_name)
|
error = view.auth_error(user_name)
|
||||||
|
|
||||||
def manage_new_account(self, *args, **kwargs):
|
def manage_new_account(self, type):
|
||||||
if self.view.new_account_dialog() == widgetUtils.YES:
|
# Generic settings for all account types.
|
||||||
location = (str(time.time())[-6:])
|
location = (str(time.time())[-6:])
|
||||||
log.debug("Creating session in the %s path" % (location,))
|
log.debug("Creating %s session in the %s path" % (type, location))
|
||||||
s = session.Session(location)
|
if type == "twitter":
|
||||||
manager.manager.add_session(location)
|
s = TwitterSession.Session(location)
|
||||||
s.get_configuration()
|
elif type == "mastodon":
|
||||||
# try:
|
s = MastodonSession.Session(location)
|
||||||
s.authorise()
|
result = s.authorise()
|
||||||
self.sessions.append(location)
|
if result == True:
|
||||||
|
self.sessions.append(dict(id=location, type=type))
|
||||||
self.view.add_new_session_to_list()
|
self.view.add_new_session_to_list()
|
||||||
s.settings.write()
|
|
||||||
# except:
|
|
||||||
# log.exception("Error authorising the session")
|
|
||||||
# self.view.show_unauthorised_error()
|
|
||||||
# return
|
|
||||||
|
|
||||||
def remove(self, *args, **kwargs):
|
def remove_account(self, index):
|
||||||
if self.view.remove_account_dialog() == widgetUtils.YES:
|
selected_account = self.sessions[index]
|
||||||
selected_account = self.sessions[self.view.get_selected()]
|
self.view.remove_session(index)
|
||||||
self.view.remove_session(self.view.get_selected())
|
self.removed_sessions.append(selected_account.get("id"))
|
||||||
self.removed_sessions.append(selected_account)
|
|
||||||
self.sessions.remove(selected_account)
|
self.sessions.remove(selected_account)
|
||||||
shutil.rmtree(path=os.path.join(paths.config_path(), selected_account), ignore_errors=True)
|
shutil.rmtree(path=os.path.join(paths.config_path(), selected_account.get("id")), ignore_errors=True)
|
||||||
|
|
||||||
|
def configuration(self):
|
||||||
def configuration(self, *args, **kwargs):
|
|
||||||
""" Opens the global settings dialogue."""
|
""" Opens the global settings dialogue."""
|
||||||
d = settings.globalSettingsController()
|
d = settings.globalSettingsController()
|
||||||
if d.response == widgetUtils.OK:
|
if d.response == widgetUtils.OK:
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
""" Base GUI (Wx) class for the Session manager module."""
|
||||||
import wx
|
import wx
|
||||||
|
from pubsub import pub
|
||||||
from multiplatform_widgets import widgets
|
from multiplatform_widgets import widgets
|
||||||
import application
|
|
||||||
|
|
||||||
class sessionManagerWindow(wx.Dialog):
|
class sessionManagerWindow(wx.Dialog):
|
||||||
|
""" Dialog that displays all session managing capabilities to users. """
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(sessionManagerWindow, self).__init__(parent=None, title=_(u"Session manager"), size=wx.DefaultSize)
|
super(sessionManagerWindow, self).__init__(parent=None, title=_(u"Session manager"), size=wx.DefaultSize)
|
||||||
panel = wx.Panel(self)
|
panel = wx.Panel(self)
|
||||||
@ -16,8 +17,11 @@ class sessionManagerWindow(wx.Dialog):
|
|||||||
listSizer.Add(self.list.list, 0, wx.ALL, 5)
|
listSizer.Add(self.list.list, 0, wx.ALL, 5)
|
||||||
sizer.Add(listSizer, 0, wx.ALL, 5)
|
sizer.Add(listSizer, 0, wx.ALL, 5)
|
||||||
self.new = wx.Button(panel, -1, _(u"New account"), size=wx.DefaultSize)
|
self.new = wx.Button(panel, -1, _(u"New account"), size=wx.DefaultSize)
|
||||||
|
self.new.Bind(wx.EVT_BUTTON, self.on_new_account)
|
||||||
self.remove = wx.Button(panel, -1, _(u"Remove account"))
|
self.remove = wx.Button(panel, -1, _(u"Remove account"))
|
||||||
|
self.remove.Bind(wx.EVT_BUTTON, self.on_remove)
|
||||||
self.configuration = wx.Button(panel, -1, _(u"Global Settings"))
|
self.configuration = wx.Button(panel, -1, _(u"Global Settings"))
|
||||||
|
self.configuration.Bind(wx.EVT_BUTTON, self.on_configuration)
|
||||||
ok = wx.Button(panel, wx.ID_OK, size=wx.DefaultSize)
|
ok = wx.Button(panel, wx.ID_OK, size=wx.DefaultSize)
|
||||||
ok.SetDefault()
|
ok.SetDefault()
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, size=wx.DefaultSize)
|
cancel = wx.Button(panel, wx.ID_CANCEL, size=wx.DefaultSize)
|
||||||
@ -42,11 +46,29 @@ class sessionManagerWindow(wx.Dialog):
|
|||||||
if self.list.get_count() == 0:
|
if self.list.get_count() == 0:
|
||||||
wx.MessageDialog(None, _(u"You need to configure an account."), _(u"Account Error"), wx.ICON_ERROR).ShowModal()
|
wx.MessageDialog(None, _(u"You need to configure an account."), _(u"Account Error"), wx.ICON_ERROR).ShowModal()
|
||||||
return
|
return
|
||||||
self.controller.do_ok()
|
|
||||||
self.EndModal(wx.ID_OK)
|
self.EndModal(wx.ID_OK)
|
||||||
|
|
||||||
def new_account_dialog(self):
|
def on_new_account(self, *args, **kwargs):
|
||||||
return 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).ShowModal()
|
menu = wx.Menu()
|
||||||
|
twitter = menu.Append(wx.ID_ANY, _("Twitter"))
|
||||||
|
mastodon = menu.Append(wx.ID_ANY, _("Mastodon"))
|
||||||
|
menu.Bind(wx.EVT_MENU, self.on_new_twitter_account, twitter)
|
||||||
|
menu.Bind(wx.EVT_MENU, self.on_new_mastodon_account, mastodon)
|
||||||
|
self.PopupMenu(menu, self.new.GetPosition())
|
||||||
|
|
||||||
|
def on_new_mastodon_account(self, *args, **kwargs):
|
||||||
|
dlg = wx.MessageDialog(self, _("You will be prompted for your Mastodon data (instance URL, email address and password) so we can authorise TWBlue in your instance. Would you like to authorise your account now?"), _(u"Authorization"), wx.YES_NO)
|
||||||
|
response = dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
||||||
|
if response == wx.ID_YES:
|
||||||
|
pub.sendMessage("sessionmanager.new_account", type="mastodon")
|
||||||
|
|
||||||
|
def on_new_twitter_account(self, *args, **kwargs):
|
||||||
|
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()
|
||||||
@ -61,8 +83,16 @@ class sessionManagerWindow(wx.Dialog):
|
|||||||
def get_response(self):
|
def get_response(self):
|
||||||
return self.ShowModal()
|
return self.ShowModal()
|
||||||
|
|
||||||
def remove_account_dialog(self):
|
def on_remove(self, *args, **kwargs):
|
||||||
return wx.MessageDialog(self, _(u"Do you really want to delete this account?"), _(u"Remove account"), wx.YES_NO).ShowModal()
|
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this account?"), _(u"Remove account"), wx.YES_NO)
|
||||||
|
response = dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
||||||
|
if response == wx.ID_YES:
|
||||||
|
selected = self.list.get_selected()
|
||||||
|
pub.sendMessage("sessionmanager.remove_account", index=selected)
|
||||||
|
|
||||||
|
def on_configuration(self, *args, **kwargs):
|
||||||
|
pub.sendMessage("sessionmanager.configuration")
|
||||||
|
|
||||||
def get_selected(self):
|
def get_selected(self):
|
||||||
return self.list.get_selected()
|
return self.list.get_selected()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
""" this package contains code related to Sessions.
|
""" this package contains code related to Sessions.
|
||||||
In TWBlue, a session module defines everything a social network needs to be used in the program."""
|
In TWBlue, a session module defines everything a social network needs to be used in the program."""
|
||||||
from __future__ import unicode_literals
|
|
||||||
# let's define a global object for storing sessions across the program.
|
# let's define a global object for storing sessions across the program.
|
||||||
sessions = {}
|
sessions = {}
|
||||||
|
@ -6,10 +6,12 @@ import output
|
|||||||
import time
|
import time
|
||||||
import sound
|
import sound
|
||||||
import logging
|
import logging
|
||||||
|
import config
|
||||||
import config_utils
|
import config_utils
|
||||||
import sqlitedict
|
import sqlitedict
|
||||||
import application
|
import application
|
||||||
from . import session_exceptions as Exceptions
|
from . import session_exceptions as Exceptions
|
||||||
|
|
||||||
log = logging.getLogger("sessionmanager.session")
|
log = logging.getLogger("sessionmanager.session")
|
||||||
|
|
||||||
class baseSession(object):
|
class baseSession(object):
|
||||||
@ -52,6 +54,13 @@ class baseSession(object):
|
|||||||
def is_logged(self):
|
def is_logged(self):
|
||||||
return self.logged
|
return self.logged
|
||||||
|
|
||||||
|
def create_session_folder(self):
|
||||||
|
path = os.path.join(paths.config_path(), self.session_id)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
log.debug("Creating %s path" % (os.path.join(paths.config_path(), path),))
|
||||||
|
os.mkdir(path)
|
||||||
|
config.app["sessions"]["sessions"].append(id)
|
||||||
|
|
||||||
def get_configuration(self):
|
def get_configuration(self):
|
||||||
""" Get settings for a session."""
|
""" Get settings for a session."""
|
||||||
file_ = "%s/session.conf" % (self.session_id,)
|
file_ = "%s/session.conf" % (self.session_id,)
|
||||||
@ -60,6 +69,9 @@ class baseSession(object):
|
|||||||
self.init_sound()
|
self.init_sound()
|
||||||
self.load_persistent_data()
|
self.load_persistent_data()
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_sound(self):
|
def init_sound(self):
|
||||||
try: self.sound = sound.soundSystem(self.settings["sound"])
|
try: self.sound = sound.soundSystem(self.settings["sound"])
|
||||||
except: pass
|
except: pass
|
||||||
|
1
src/sessions/mastodon/__init__.py
Normal file
1
src/sessions/mastodon/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
51
src/sessions/mastodon/compose.py
Normal file
51
src/sessions/mastodon/compose.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import arrow
|
||||||
|
import languageHandler
|
||||||
|
from . import utils, templates
|
||||||
|
|
||||||
|
def compose_post(post, db, relative_times, show_screen_names):
|
||||||
|
if show_screen_names == False:
|
||||||
|
user = post.account.get("display_name")
|
||||||
|
if user == "":
|
||||||
|
user = post.account.get("username")
|
||||||
|
else:
|
||||||
|
user = post.account.get("acct")
|
||||||
|
original_date = arrow.get(post.created_at)
|
||||||
|
if relative_times:
|
||||||
|
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||||
|
else:
|
||||||
|
ts = original_date.shift(hours=db["utc_offset"]).format(_("dddd, MMMM D, YYYY H:m"), locale=languageHandler.curLang[:2])
|
||||||
|
if post.reblog != None:
|
||||||
|
text = _("Boosted from @{}: {}").format(post.reblog.account.acct, templates.process_text(post.reblog))
|
||||||
|
else:
|
||||||
|
text = templates.process_text(post)
|
||||||
|
source = post.get("application", "")
|
||||||
|
# "" means remote user, None for legacy apps so we should cover both sides.
|
||||||
|
if source != None and source != "":
|
||||||
|
source = source.get("name", "")
|
||||||
|
else:
|
||||||
|
source = ""
|
||||||
|
return [user+", ", text, ts+", ", source]
|
||||||
|
|
||||||
|
def compose_user(user, db, relative_times=True, show_screen_names=False):
|
||||||
|
original_date = arrow.get(user.created_at)
|
||||||
|
if relative_times:
|
||||||
|
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||||
|
else:
|
||||||
|
ts = original_date.shift(hours=db["utc_offset"]).format(_("dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
||||||
|
name = user.display_name
|
||||||
|
if name == "":
|
||||||
|
name = user.get("username")
|
||||||
|
return [_("%s (@%s). %s followers, %s following, %s posts. Joined %s") % (name, user.acct, user.followers_count, user.following_count, user.statuses_count, ts)]
|
||||||
|
|
||||||
|
def compose_conversation(conversation, db, relative_times, show_screen_names):
|
||||||
|
users = []
|
||||||
|
for account in conversation.accounts:
|
||||||
|
if account.display_name != "":
|
||||||
|
users.append(account.display_name)
|
||||||
|
else:
|
||||||
|
users.append(account.username)
|
||||||
|
users = ", ".join(users)
|
||||||
|
last_post = compose_post(conversation.last_status, db, relative_times, show_screen_names)
|
||||||
|
text = _("Last message from {}: {}").format(last_post[0], last_post[1])
|
||||||
|
return [users, text, last_post[-2], last_post[-1]]
|
290
src/sessions/mastodon/session.py
Normal file
290
src/sessions/mastodon/session.py
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import paths
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import webbrowser
|
||||||
|
import wx
|
||||||
|
import mastodon
|
||||||
|
import config
|
||||||
|
import config_utils
|
||||||
|
import output
|
||||||
|
import application
|
||||||
|
from mastodon import MastodonError, MastodonNotFoundError, MastodonUnauthorizedError
|
||||||
|
from pubsub import pub
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
|
from sessions import base
|
||||||
|
from sessions.mastodon import utils, streaming
|
||||||
|
from .wxUI import authorisationDialog
|
||||||
|
|
||||||
|
log = logging.getLogger("sessions.mastodonSession")
|
||||||
|
|
||||||
|
MASTODON_VERSION = "4.0.1"
|
||||||
|
|
||||||
|
class Session(base.baseSession):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(Session, self).__init__(*args, **kwargs)
|
||||||
|
self.config_spec = "mastodon.defaults"
|
||||||
|
self.supported_languages = []
|
||||||
|
self.type = "mastodon"
|
||||||
|
self.db["pagination_info"] = dict()
|
||||||
|
self.char_limit = 500
|
||||||
|
pub.subscribe(self.on_status, "mastodon.status_received")
|
||||||
|
pub.subscribe(self.on_status_updated, "mastodon.status_updated")
|
||||||
|
pub.subscribe(self.on_notification, "mastodon.notification_received")
|
||||||
|
|
||||||
|
def login(self, verify_credentials=True):
|
||||||
|
if self.settings["mastodon"]["access_token"] != None and self.settings["mastodon"]["instance"] != None:
|
||||||
|
try:
|
||||||
|
log.debug("Logging in to Mastodon instance {}...".format(self.settings["mastodon"]["instance"]))
|
||||||
|
self.api = mastodon.Mastodon(access_token=self.settings["mastodon"]["access_token"], api_base_url=self.settings["mastodon"]["instance"], mastodon_version=MASTODON_VERSION)
|
||||||
|
if verify_credentials == True:
|
||||||
|
credentials = self.api.account_verify_credentials()
|
||||||
|
self.db["user_name"] = credentials["username"]
|
||||||
|
self.db["user_id"] = credentials["id"]
|
||||||
|
self.settings["mastodon"]["user_name"] = credentials["username"]
|
||||||
|
self.logged = True
|
||||||
|
log.debug("Logged.")
|
||||||
|
self.counter = 0
|
||||||
|
except IOError:
|
||||||
|
log.error("The login attempt failed.")
|
||||||
|
self.logged = False
|
||||||
|
else:
|
||||||
|
self.logged = False
|
||||||
|
raise Exceptions.RequireCredentialsSessionError
|
||||||
|
|
||||||
|
def authorise(self):
|
||||||
|
if self.logged == True:
|
||||||
|
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
||||||
|
authorisation_dialog = wx.TextEntryDialog(None, _("Please enter your instance URL."), _("Mastodon instance"))
|
||||||
|
answer = authorisation_dialog.ShowModal()
|
||||||
|
instance = authorisation_dialog.GetValue()
|
||||||
|
authorisation_dialog.Destroy()
|
||||||
|
if answer != wx.ID_OK:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
client_id, client_secret = mastodon.Mastodon.create_app("TWBlue", api_base_url=authorisation_dialog.GetValue(), website="https://twblue.es")
|
||||||
|
temporary_api = mastodon.Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=instance, mastodon_version=MASTODON_VERSION)
|
||||||
|
auth_url = temporary_api.auth_request_url()
|
||||||
|
except MastodonError:
|
||||||
|
dlg = wx.MessageDialog(None, _("We could not connect to your mastodon instance. Please verify that the domain exists and the instance is accessible via a web browser."), _("Instance error"), wx.ICON_ERROR)
|
||||||
|
dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
||||||
|
return
|
||||||
|
webbrowser.open_new_tab(auth_url)
|
||||||
|
verification_dialog = wx.TextEntryDialog(None, _("Enter the verification code"), _("PIN code authorization"))
|
||||||
|
answer = verification_dialog.ShowModal()
|
||||||
|
code = verification_dialog.GetValue()
|
||||||
|
verification_dialog.Destroy()
|
||||||
|
if answer != wx.ID_OK:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
access_token = temporary_api.log_in(code=verification_dialog.GetValue())
|
||||||
|
except MastodonError:
|
||||||
|
dlg = wx.MessageDialog(None, _("We could not authorice your mastodon 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
|
||||||
|
self.create_session_folder()
|
||||||
|
self.get_configuration()
|
||||||
|
self.settings["mastodon"]["access_token"] = access_token
|
||||||
|
self.settings["mastodon"]["instance"] = instance
|
||||||
|
self.settings.write()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_user_info(self):
|
||||||
|
""" Retrieves some information required by TWBlue for setup."""
|
||||||
|
# retrieve the current user's UTC offset so we can calculate dates properly.
|
||||||
|
offset = time.timezone if (time.localtime().tm_isdst == 0) else time.altzone
|
||||||
|
offset = offset / 60 / 60 * -1
|
||||||
|
self.db["utc_offset"] = offset
|
||||||
|
if len(self.supported_languages) == 0:
|
||||||
|
self.supported_languages = self.api.instance().languages
|
||||||
|
self.get_lists()
|
||||||
|
self.get_muted_users()
|
||||||
|
# determine instance custom characters limit.
|
||||||
|
instance = self.api.instance()
|
||||||
|
if hasattr(instance, "max_toot_chars"):
|
||||||
|
self.char_limit = instance.max_toot_chars
|
||||||
|
self.settings.write()
|
||||||
|
|
||||||
|
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.api.lists()
|
||||||
|
|
||||||
|
def get_muted_users(self):
|
||||||
|
### ToDo: Use a function to retrieve all muted users.
|
||||||
|
self.db["muted_users"] = self.api.mutes()
|
||||||
|
|
||||||
|
def get_user_alias(self, user):
|
||||||
|
aliases = self.settings.get("user-aliases")
|
||||||
|
if aliases == None:
|
||||||
|
log.error("Aliases are not defined for this config spec.")
|
||||||
|
return user.name
|
||||||
|
user_alias = aliases.get(user.id_str)
|
||||||
|
if user_alias != None:
|
||||||
|
return user_alias
|
||||||
|
return user.name
|
||||||
|
|
||||||
|
def order_buffer(self, name, data, ignore_older=False):
|
||||||
|
num = 0
|
||||||
|
last_id = None
|
||||||
|
if self.db.get(name) == None:
|
||||||
|
self.db[name] = []
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
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 update_item(self, name, item):
|
||||||
|
if name not in self.db:
|
||||||
|
return False
|
||||||
|
items = self.db[name]
|
||||||
|
if type(items) != list:
|
||||||
|
return False
|
||||||
|
# determine item position in buffer.
|
||||||
|
item_position = next((x for x in range(len(items)) if items[x].id == item.id), None)
|
||||||
|
if item_position != None:
|
||||||
|
self.db[name][item_position] = item
|
||||||
|
return item_position
|
||||||
|
return False
|
||||||
|
|
||||||
|
def api_call(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.api, call_name)(*args, **kwargs)
|
||||||
|
finished = True
|
||||||
|
except MastodonError as e:
|
||||||
|
output.speak(str(e))
|
||||||
|
val = None
|
||||||
|
if type(e) != MastodonNotFoundError and type(e) != MastodonUnauthorizedError :
|
||||||
|
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 send_post(self, reply_to=None, users=None, visibility=None, posts=[]):
|
||||||
|
""" Convenience function to send a thread. """
|
||||||
|
in_reply_to_id = reply_to
|
||||||
|
for obj in posts:
|
||||||
|
text = obj.get("text")
|
||||||
|
if len(obj["attachments"]) == 0:
|
||||||
|
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"])
|
||||||
|
if item != None:
|
||||||
|
in_reply_to_id = item["id"]
|
||||||
|
else:
|
||||||
|
media_ids = []
|
||||||
|
poll = None
|
||||||
|
if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll":
|
||||||
|
poll = self.api.make_poll(options=obj["attachments"][0]["options"], expires_in=obj["attachments"][0]["expires_in"], multiple=obj["attachments"][0]["multiple"], hide_totals=obj["attachments"][0]["hide_totals"])
|
||||||
|
else:
|
||||||
|
for i in obj["attachments"]:
|
||||||
|
img = self.api_call("media_post", media_file=i["file"], description=i["description"])
|
||||||
|
media_ids.append(img.id)
|
||||||
|
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"])
|
||||||
|
if item != None:
|
||||||
|
in_reply_to_id = item["id"]
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
instance = self.settings["mastodon"]["instance"]
|
||||||
|
instance = instance.replace("https://", "")
|
||||||
|
user = self.settings["mastodon"]["user_name"]
|
||||||
|
return "Mastodon: {}@{}".format(user, instance)
|
||||||
|
|
||||||
|
def start_streaming(self):
|
||||||
|
if config.app["app-settings"]["no_streaming"]:
|
||||||
|
return
|
||||||
|
listener = streaming.StreamListener(session_name=self.get_name(), user_id=self.db["user_id"])
|
||||||
|
self.user_stream = self.api.stream_user(listener, run_async=True)
|
||||||
|
self.direct_stream = self.api.stream_direct(listener, run_async=True)
|
||||||
|
|
||||||
|
def stop_streaming(self):
|
||||||
|
if config.app["app-settings"]["no_streaming"]:
|
||||||
|
return
|
||||||
|
# if hasattr(self, "user_stream"):
|
||||||
|
# self.user_stream.close()
|
||||||
|
# log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
||||||
|
# if hasattr(self, "direct_stream"):
|
||||||
|
# self.direct_stream.close()
|
||||||
|
# log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
||||||
|
|
||||||
|
def check_streams(self):
|
||||||
|
if config.app["app-settings"]["no_streaming"]:
|
||||||
|
return
|
||||||
|
if not hasattr(self, "user_stream"):
|
||||||
|
return
|
||||||
|
if self.user_stream.is_alive() == False or self.user_stream.is_receiving() == False or self.direct_stream.is_alive() == False or self.direct_stream.is_receiving() == False:
|
||||||
|
self.start_streaming()
|
||||||
|
|
||||||
|
def check_buffers(self, status):
|
||||||
|
buffers = []
|
||||||
|
buffers.append("home_timeline")
|
||||||
|
if status.account.id == self.db["user_id"]:
|
||||||
|
buffers.append("sent")
|
||||||
|
return buffers
|
||||||
|
|
||||||
|
def on_status(self, status, session_name):
|
||||||
|
# Discard processing the status if the streaming sends a tweet for another account.
|
||||||
|
if self.get_name() != session_name:
|
||||||
|
return
|
||||||
|
buffers = self.check_buffers(status)
|
||||||
|
for b in buffers[::]:
|
||||||
|
num = self.order_buffer(b, [status])
|
||||||
|
if num == 0:
|
||||||
|
buffers.remove(b)
|
||||||
|
pub.sendMessage("mastodon.new_item", session_name=self.get_name(), item=status, _buffers=buffers)
|
||||||
|
|
||||||
|
def on_status_updated(self, status, session_name):
|
||||||
|
# Discard processing the status if the streaming sends a tweet for another account.
|
||||||
|
if self.get_name() != session_name:
|
||||||
|
return
|
||||||
|
buffers = {}
|
||||||
|
for b in list(self.db.keys()):
|
||||||
|
updated = self.update_item(b, status)
|
||||||
|
if updated != False:
|
||||||
|
buffers[b] = updated
|
||||||
|
pub.sendMessage("mastodon.updated_item", session_name=self.get_name(), item=status, _buffers=buffers)
|
||||||
|
|
||||||
|
def on_notification(self, notification, session_name):
|
||||||
|
# Discard processing the notification if the streaming sends a tweet for another account.
|
||||||
|
if self.get_name() != session_name:
|
||||||
|
return
|
||||||
|
buffers = []
|
||||||
|
obj = None
|
||||||
|
if notification.type == "mention":
|
||||||
|
buffers = ["mentions"]
|
||||||
|
obj = notification.status
|
||||||
|
elif notification.type == "follow":
|
||||||
|
buffers = ["followers"]
|
||||||
|
obj = notification.account
|
||||||
|
for b in buffers[::]:
|
||||||
|
num = self.order_buffer(b, [obj])
|
||||||
|
if num == 0:
|
||||||
|
buffers.remove(b)
|
||||||
|
pub.sendMessage("mastodon.new_item", session_name=self.get_name(), item=obj, _buffers=buffers)
|
25
src/sessions/mastodon/streaming.py
Normal file
25
src/sessions/mastodon/streaming.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import mastodon
|
||||||
|
from pubsub import pub
|
||||||
|
|
||||||
|
class StreamListener(mastodon.StreamListener):
|
||||||
|
|
||||||
|
def __init__(self, session_name, user_id):
|
||||||
|
self.session_name = session_name
|
||||||
|
self.user_id = user_id
|
||||||
|
super(StreamListener, self).__init__()
|
||||||
|
|
||||||
|
def on_update(self, status):
|
||||||
|
pub.sendMessage("mastodon.status_received", status=status, session_name=self.session_name)
|
||||||
|
|
||||||
|
def on_status_update(self, status):
|
||||||
|
pub.sendMessage("mastodon.status_updated", status=status, session_name=self.session_name)
|
||||||
|
|
||||||
|
def on_conversation(self, conversation):
|
||||||
|
pub.sendMessage("mastodon.conversation_received", conversation=conversation, session_name=self.session_name)
|
||||||
|
|
||||||
|
def on_notification(self, notification):
|
||||||
|
pub.sendMessage("mastodon.notification_received", notification=notification, session_name=self.session_name)
|
||||||
|
|
||||||
|
def on_unknown_event(self, event, payload):
|
||||||
|
log.error("Unknown event: {} with payload as {}".format(event, payload))
|
137
src/sessions/mastodon/templates.py
Normal file
137
src/sessions/mastodon/templates.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import arrow
|
||||||
|
import languageHandler
|
||||||
|
from string import Template
|
||||||
|
from . import utils, compose
|
||||||
|
|
||||||
|
# Define variables that would be available for all template objects.
|
||||||
|
# This will be used for the edit template dialog.
|
||||||
|
# Available variables for post objects.
|
||||||
|
# safe_text will be the content warning in case a post contains one, text will always be the full text, no matter if has a content warning or not.
|
||||||
|
post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe_text", "text", "image_descriptions", "visibility"]
|
||||||
|
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
|
||||||
|
conversation_variables = ["users", "last_post"]
|
||||||
|
|
||||||
|
# Default, translatable templates.
|
||||||
|
post_default_template = _("$display_name, $text $image_descriptions $date. $source")
|
||||||
|
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
|
||||||
|
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
|
||||||
|
|
||||||
|
def process_date(field, relative_times=True, offset_hours=0):
|
||||||
|
original_date = arrow.get(field)
|
||||||
|
if relative_times == True:
|
||||||
|
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||||
|
else:
|
||||||
|
ts = original_date.shift(hours=offset_hours).format(_("dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
||||||
|
return ts
|
||||||
|
|
||||||
|
def process_text(post, safe=True):
|
||||||
|
# text = utils.clean_mentions(utils.StripChars(text))
|
||||||
|
if safe == True and post.sensitive == True and post.spoiler_text != "":
|
||||||
|
return _("Content warning: {}").format(post.spoiler_text)
|
||||||
|
return utils.html_filter(post.content)
|
||||||
|
|
||||||
|
def process_image_descriptions(media_attachments):
|
||||||
|
""" Attempt to extract information for image descriptions. """
|
||||||
|
image_descriptions = []
|
||||||
|
for media in media_attachments:
|
||||||
|
if media.get("description") != None and media.get("description") != "":
|
||||||
|
image_descriptions.append(media.get("description"))
|
||||||
|
idescriptions = ""
|
||||||
|
for image in image_descriptions:
|
||||||
|
idescriptions = idescriptions + _("Image description: {}").format(image) + "\n"
|
||||||
|
return idescriptions
|
||||||
|
|
||||||
|
def remove_unneeded_variables(template, variables):
|
||||||
|
for variable in variables:
|
||||||
|
template = re.sub("\$"+variable, "", template)
|
||||||
|
return template
|
||||||
|
|
||||||
|
def render_post(post, template, relative_times=False, offset_hours=0):
|
||||||
|
""" Renders any given post according to the passed template.
|
||||||
|
Available data for posts 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.
|
||||||
|
$safe_text: Safe text to display. If a content warning is applied in posts, display those instead of the whole post.
|
||||||
|
$text: Toot text. This always displays the full text, even if there is a content warning present.
|
||||||
|
$image_descriptions: Information regarding image descriptions added by twitter users.
|
||||||
|
$visibility: post's visibility: public, not listed, followers only or direct.
|
||||||
|
"""
|
||||||
|
global post_variables
|
||||||
|
available_data = dict()
|
||||||
|
created_at = process_date(post.created_at, relative_times, offset_hours)
|
||||||
|
available_data.update(date=created_at)
|
||||||
|
# user.
|
||||||
|
display_name = post.account.display_name
|
||||||
|
if display_name == "":
|
||||||
|
display_name = post.account.username
|
||||||
|
available_data.update(display_name=display_name, screen_name=post.account.acct)
|
||||||
|
# Source client from where tweet was originated.
|
||||||
|
source = ""
|
||||||
|
if hasattr(post, "application") and post.application != None:
|
||||||
|
available_data.update(source=post.application.get("name"))
|
||||||
|
if post.reblog != None:
|
||||||
|
text = _("Boosted from @{}: {}").format(post.reblog.account.acct, process_text(post.reblog, safe=False), )
|
||||||
|
safe_text = _("Boosted from @{}: {}").format(post.reblog.account.acct, process_text(post.reblog), )
|
||||||
|
else:
|
||||||
|
text = process_text(post, safe=False)
|
||||||
|
safe_text = process_text(post)
|
||||||
|
visibility_settings = dict(public=_("Public"), unlisted=_("Not listed"), private=_("Followers only"), direct=_("Direct"))
|
||||||
|
visibility = visibility_settings.get(post.visibility)
|
||||||
|
available_data.update(lang=post.language, text=text, safe_text=safe_text, visibility=visibility)
|
||||||
|
# process image descriptions
|
||||||
|
image_descriptions = ""
|
||||||
|
if post.reblog != None:
|
||||||
|
image_descriptions = process_image_descriptions(post.reblog.media_attachments)
|
||||||
|
else:
|
||||||
|
image_descriptions = process_image_descriptions(post.media_attachments)
|
||||||
|
if image_descriptions != "":
|
||||||
|
available_data.update(image_descriptions=image_descriptions)
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
result = remove_unneeded_variables(result, post_variables)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def render_user(user, template, relative_times=True, offset_hours=0):
|
||||||
|
""" Renders persons 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.
|
||||||
|
$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.
|
||||||
|
$posts: 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
|
||||||
|
display_name = user.display_name
|
||||||
|
if display_name == "":
|
||||||
|
display_name = user.username
|
||||||
|
available_data = dict(display_name=display_name, screen_name=user.acct, followers=user.followers_count, following=user.following_count, posts=user.statuses_count)
|
||||||
|
# Nullable values.
|
||||||
|
nullables = ["description"]
|
||||||
|
for nullable in nullables:
|
||||||
|
if hasattr(user, nullable) and getattr(user, nullable) != None:
|
||||||
|
available_data[nullable] = getattr(user, nullable)
|
||||||
|
created_at = process_date(user.created_at, relative_times=relative_times, offset_hours=offset_hours)
|
||||||
|
available_data.update(created_at=created_at)
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
result = remove_unneeded_variables(result, person_variables)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def render_conversation(conversation, template, post_template, relative_times=False, offset_hours=0):
|
||||||
|
users = []
|
||||||
|
for account in conversation.accounts:
|
||||||
|
if account.display_name != "":
|
||||||
|
users.append(account.display_name)
|
||||||
|
else:
|
||||||
|
users.append(account.username)
|
||||||
|
users = ", ".join(users)
|
||||||
|
last_post = render_post(conversation.last_status, post_template, relative_times=relative_times, offset_hours=offset_hours)
|
||||||
|
available_data = dict(users=users, last_post=last_post)
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
result = remove_unneeded_variables(result, conversation_variables)
|
||||||
|
return result
|
60
src/sessions/mastodon/utils.py
Normal file
60
src/sessions/mastodon/utils.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import re
|
||||||
|
from html.parser import HTMLParser
|
||||||
|
|
||||||
|
url_re = re.compile('<a\s*href=[\'|"](.*?)[\'"].*?>')
|
||||||
|
|
||||||
|
class HTMLFilter(HTMLParser):
|
||||||
|
text = ""
|
||||||
|
def handle_data(self, data):
|
||||||
|
self.text += data
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
if tag == "br":
|
||||||
|
self.text = self.text+"\n"
|
||||||
|
|
||||||
|
def html_filter(data):
|
||||||
|
f = HTMLFilter()
|
||||||
|
f.feed(data)
|
||||||
|
return f.text
|
||||||
|
|
||||||
|
def find_item(item, listItems):
|
||||||
|
for i in range(0, len(listItems)):
|
||||||
|
if listItems[i].id == item.id:
|
||||||
|
return i
|
||||||
|
if hasattr(item, "reblog") and item.reblog != None and item.reblog.id == listItems[i].id:
|
||||||
|
return i
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_audio_or_video(post):
|
||||||
|
if post.reblog != None:
|
||||||
|
return is_audio_or_video(post.reblog)
|
||||||
|
# Checks firstly for Mastodon native videos and audios.
|
||||||
|
for media in post.media_attachments:
|
||||||
|
if media["type"] == "video" or media["type"] == "audio":
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_image(post):
|
||||||
|
if post.reblog != None:
|
||||||
|
return is_image(post.reblog)
|
||||||
|
# Checks firstly for Mastodon native videos and audios.
|
||||||
|
for media in post.media_attachments:
|
||||||
|
if media["type"] == "gifv" or media["type"] == "image":
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_media_urls(post):
|
||||||
|
if hasattr(post, "reblog") and post.reblog != None:
|
||||||
|
return get_media_urls(post.reblog)
|
||||||
|
urls = []
|
||||||
|
for media in post.media_attachments:
|
||||||
|
if media.get("type") == "audio" or media.get("type") == "video":
|
||||||
|
urls.append(media.get("url"))
|
||||||
|
return urls
|
||||||
|
|
||||||
|
def find_urls(post, include_tags=False):
|
||||||
|
urls = url_re.findall(post.content)
|
||||||
|
if include_tags == False:
|
||||||
|
for tag in post.tags:
|
||||||
|
for url in urls[::]:
|
||||||
|
if url.lower().endswith("/tags/"+tag["name"]):
|
||||||
|
urls.remove(url)
|
||||||
|
return urls
|
35
src/sessions/mastodon/wxUI.py
Normal file
35
src/sessions/mastodon/wxUI.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# -*- 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)
|
||||||
|
static1 = wx.StaticText(panel, wx.NewId(), _("URL of mastodon instance:"))
|
||||||
|
self.instance = wx.TextCtrl(panel, -1)
|
||||||
|
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer1.Add(static1, 0, wx.ALL, 5)
|
||||||
|
sizer1.Add(self.instance, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(sizer1, 0, wx.ALL, 5)
|
||||||
|
static2 = wx.StaticText(panel, wx.NewId(), _("Email address:"))
|
||||||
|
self.email = wx.TextCtrl(panel, -1)
|
||||||
|
sizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer2.Add(static2, 0, wx.ALL, 5)
|
||||||
|
sizer2.Add(self.email, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(sizer2, 0, wx.ALL, 5)
|
||||||
|
static3 = wx.StaticText(panel, wx.NewId(), _("Password:"))
|
||||||
|
self.password = wx.TextCtrl(panel, -1)
|
||||||
|
sizer3 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer3.Add(static3, 0, wx.ALL, 5)
|
||||||
|
sizer3.Add(self.password, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(sizer3, 0, wx.ALL, 5)
|
||||||
|
self.ok = wx.Button(panel, wx.ID_OK)
|
||||||
|
self.cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||||
|
sizer4 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer4.Add(self.ok, 0, wx.ALL, 5)
|
||||||
|
sizer4.Add(self.cancel, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(sizer4, 0, wx.ALL, 5)
|
||||||
|
panel.SetSizer(sizer)
|
||||||
|
min = sizer.CalcMin()
|
||||||
|
self.SetClientSize(min)
|
@ -5,13 +5,13 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import wx
|
import wx
|
||||||
|
import tweepy
|
||||||
import demoji
|
import demoji
|
||||||
import config
|
import config
|
||||||
import output
|
import output
|
||||||
import application
|
import application
|
||||||
import appkeys
|
import appkeys
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
import tweepy
|
|
||||||
from tweepy.errors import TweepyException, Forbidden, NotFound
|
from tweepy.errors import TweepyException, Forbidden, NotFound
|
||||||
from tweepy.models import User as UserModel
|
from tweepy.models import User as UserModel
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
@ -106,10 +106,8 @@ class Session(base.baseSession):
|
|||||||
else: objects.insert(0, i)
|
else: objects.insert(0, i)
|
||||||
incoming = incoming+1
|
incoming = incoming+1
|
||||||
self.db["direct_messages"] = objects
|
self.db["direct_messages"] = objects
|
||||||
|
|
||||||
self.db["sent_direct_messages"] = sent_objects
|
self.db["sent_direct_messages"] = sent_objects
|
||||||
pub.sendMessage("sent-dms-updated", total=sent, account=self.db["user_name"])
|
pub.sendMessage("twitter.sent_dms_updated", total=sent, session_name=self.get_name())
|
||||||
|
|
||||||
return incoming
|
return incoming
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -127,8 +125,8 @@ class Session(base.baseSession):
|
|||||||
# If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object.
|
# If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object.
|
||||||
self.deleted_users = {}
|
self.deleted_users = {}
|
||||||
self.type = "twitter"
|
self.type = "twitter"
|
||||||
pub.subscribe(self.handle_new_status, "newStatus")
|
pub.subscribe(self.handle_new_status, "twitter.new_status")
|
||||||
pub.subscribe(self.handle_connected, "streamConnected")
|
pub.subscribe(self.handle_connected, "twitter.stream_connected")
|
||||||
|
|
||||||
# @_require_configuration
|
# @_require_configuration
|
||||||
def login(self, verify_credentials=True):
|
def login(self, verify_credentials=True):
|
||||||
@ -137,8 +135,7 @@ class Session(base.baseSession):
|
|||||||
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
|
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
|
||||||
try:
|
try:
|
||||||
log.debug("Logging in to twitter...")
|
log.debug("Logging in to twitter...")
|
||||||
self.auth = tweepy.OAuth1UserHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
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.auth.set_access_token(self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"])
|
|
||||||
self.twitter = tweepy.API(self.auth)
|
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"])
|
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:
|
if verify_credentials == True:
|
||||||
@ -153,37 +150,32 @@ class Session(base.baseSession):
|
|||||||
self.logged = False
|
self.logged = False
|
||||||
raise Exceptions.RequireCredentialsSessionError
|
raise Exceptions.RequireCredentialsSessionError
|
||||||
|
|
||||||
# @_require_configuration
|
|
||||||
def authorise(self):
|
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()"""
|
""" 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:
|
if self.logged == True:
|
||||||
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
||||||
else:
|
auth = tweepy.OAuth1UserHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
||||||
self.auth = tweepy.OAuth1UserHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
redirect_url = auth.get_authorization_url()
|
||||||
redirect_url = self.auth.get_authorization_url()
|
|
||||||
webbrowser.open_new_tab(redirect_url)
|
webbrowser.open_new_tab(redirect_url)
|
||||||
self.authorisation_dialog = authorisationDialog()
|
verification_dialog = wx.TextEntryDialog(None, _("Enter your PIN code here"), _("Authorising account..."))
|
||||||
self.authorisation_dialog.cancel.Bind(wx.EVT_BUTTON, self.authorisation_cancelled)
|
answer = verification_dialog.ShowModal()
|
||||||
self.authorisation_dialog.ok.Bind(wx.EVT_BUTTON, self.authorisation_accepted)
|
code = verification_dialog.GetValue()
|
||||||
self.authorisation_dialog.ShowModal()
|
verification_dialog.Destroy()
|
||||||
|
if answer != wx.ID_OK:
|
||||||
def verify_authorisation(self, pincode):
|
return
|
||||||
self.auth.get_access_token(pincode)
|
try:
|
||||||
self.settings["twitter"]["user_key"] = self.auth.access_token
|
auth.get_access_token(code)
|
||||||
self.settings["twitter"]["user_secret"] = self.auth.access_token_secret
|
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()
|
self.settings.write()
|
||||||
del self.auth
|
return True
|
||||||
|
|
||||||
def authorisation_cancelled(self, *args, **kwargs):
|
|
||||||
""" Destroy the authorization dialog. """
|
|
||||||
self.authorisation_dialog.Destroy()
|
|
||||||
del self.authorisation_dialog
|
|
||||||
|
|
||||||
def authorisation_accepted(self, *args, **kwargs):
|
|
||||||
""" Gets the PIN code entered by user and validate it through Twitter."""
|
|
||||||
pincode = self.authorisation_dialog.text.GetValue()
|
|
||||||
self.verify_authorisation(pincode)
|
|
||||||
self.authorisation_dialog.Destroy()
|
|
||||||
|
|
||||||
def api_call(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs):
|
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.
|
""" 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.
|
||||||
@ -556,7 +548,7 @@ class Session(base.baseSession):
|
|||||||
def start_streaming(self):
|
def start_streaming(self):
|
||||||
if config.app["app-settings"]["no_streaming"]:
|
if config.app["app-settings"]["no_streaming"]:
|
||||||
return
|
return
|
||||||
self.stream = streaming.Stream(twitter_api=self.twitter, user=self.db["user_name"], user_id=self.db["user_id"], muted_users=self.db["muted_users"], consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"], chunk_size=1025)
|
self.stream = 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)
|
self.stream_thread = call_threaded(self.stream.filter, follow=self.stream.users, stall_warnings=True)
|
||||||
|
|
||||||
def stop_streaming(self):
|
def stop_streaming(self):
|
||||||
@ -566,12 +558,12 @@ class Session(base.baseSession):
|
|||||||
self.stream.running = False
|
self.stream.running = False
|
||||||
log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
||||||
|
|
||||||
def handle_new_status(self, status, user):
|
def handle_new_status(self, status, session_name):
|
||||||
""" Handles a new status present in the Streaming API. """
|
""" Handles a new status present in the Streaming API. """
|
||||||
if self.logged == False:
|
if self.logged == False:
|
||||||
return
|
return
|
||||||
# Discard processing the status if the streaming sends a tweet for another account.
|
# Discard processing the status if the streaming sends a tweet for another account.
|
||||||
if self.db["user_name"] != user:
|
if self.get_name() != session_name:
|
||||||
return
|
return
|
||||||
# the Streaming API sends non-extended tweets with an optional parameter "extended_tweets" which contains full_text and other data.
|
# 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.
|
# so we have to make sure we check it before processing the normal status.
|
||||||
@ -608,7 +600,7 @@ class Session(base.baseSession):
|
|||||||
status = self.check_quoted_status(status)
|
status = self.check_quoted_status(status)
|
||||||
status = self.check_long_tweet(status)
|
status = self.check_long_tweet(status)
|
||||||
# Send it to the main controller object.
|
# Send it to the main controller object.
|
||||||
pub.sendMessage("newTweet", data=status, user=self.db["user_name"], _buffers=buffers_to_send)
|
pub.sendMessage("twitter.new_tweet", data=status, session_name=self.get_name(), _buffers=buffers_to_send)
|
||||||
|
|
||||||
def check_streams(self):
|
def check_streams(self):
|
||||||
if config.app["app-settings"]["no_streaming"]:
|
if config.app["app-settings"]["no_streaming"]:
|
||||||
@ -619,11 +611,11 @@ class Session(base.baseSession):
|
|||||||
if self.stream.running == False:
|
if self.stream.running == False:
|
||||||
self.start_streaming()
|
self.start_streaming()
|
||||||
|
|
||||||
def handle_connected(self, user):
|
def handle_connected(self, session_name):
|
||||||
if self.logged == False:
|
if self.logged == False:
|
||||||
return
|
return
|
||||||
if user != self.db["user_name"]:
|
if session_name != self.get_name():
|
||||||
log.debug("Connected streaming endpoint on account {}".format(user))
|
log.debug("Connected streaming endpoint on session {}".format(session_name))
|
||||||
|
|
||||||
def send_tweet(self, *tweets):
|
def send_tweet(self, *tweets):
|
||||||
""" Convenience function to send a thread. """
|
""" Convenience function to send a thread. """
|
||||||
@ -674,4 +666,10 @@ class Session(base.baseSession):
|
|||||||
else:
|
else:
|
||||||
sent_dms.insert(0, item)
|
sent_dms.insert(0, item)
|
||||||
self.db["sent_direct_messages"] = sent_dms
|
self.db["sent_direct_messages"] = sent_dms
|
||||||
pub.sendMessage("sent-dm", data=item, user=self.db["user_name"])
|
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"])
|
@ -14,13 +14,13 @@ log = logging.getLogger("sessions.twitter.streaming")
|
|||||||
|
|
||||||
class Stream(tweepy.Stream):
|
class Stream(tweepy.Stream):
|
||||||
|
|
||||||
def __init__(self, twitter_api, user, user_id, muted_users=[], *args, **kwargs):
|
def __init__(self, twitter_api, session_name, user_id, muted_users=[], *args, **kwargs):
|
||||||
super(Stream, self).__init__(*args, **kwargs)
|
super(Stream, self).__init__(*args, **kwargs)
|
||||||
log.debug("Starting streaming listener for account {}".format(user))
|
log.debug("Starting streaming listener for session {}".format(session_name))
|
||||||
self.started = False
|
self.started = False
|
||||||
self.users = []
|
self.users = []
|
||||||
self.api = twitter_api
|
self.api = twitter_api
|
||||||
self.user = user
|
self.session_name = session_name
|
||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
friends = self.api.get_friend_ids()
|
friends = self.api.get_friend_ids()
|
||||||
log.debug("Retrieved {} friends to add to the streaming listener.".format(len(friends)))
|
log.debug("Retrieved {} friends to add to the streaming listener.".format(len(friends)))
|
||||||
@ -33,15 +33,15 @@ class Stream(tweepy.Stream):
|
|||||||
log.debug("Streaming listener started with {} users to follow.".format(len(self.users)))
|
log.debug("Streaming listener started with {} users to follow.".format(len(self.users)))
|
||||||
|
|
||||||
def on_connect(self):
|
def on_connect(self):
|
||||||
pub.sendMessage("streamConnected", user=self.user)
|
pub.sendMessage("twitter.stream_connected", session_name=self.session_name)
|
||||||
|
|
||||||
def on_exception(self, ex):
|
def on_exception(self, ex):
|
||||||
log.exception("Exception received on streaming endpoint for user {}".format(self.user))
|
log.exception("Exception received on streaming endpoint for session {}".format(self.session_name))
|
||||||
|
|
||||||
def on_status(self, status):
|
def on_status(self, status):
|
||||||
""" Checks data arriving as a tweet. """
|
""" Checks data arriving as a tweet. """
|
||||||
# Hide replies to users not followed by current account.
|
# Hide replies to users not followed by current account.
|
||||||
if status.in_reply_to_user_id_str != None and status.in_reply_to_user_id_str not in self.users and status.user.screen_name != self.user:
|
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
|
return
|
||||||
if status.user.id_str in self.users:
|
if status.user.id_str in self.users:
|
||||||
pub.sendMessage("newStatus", status=status, user=self.user)
|
pub.sendMessage("twitter.new_status", status=status, session_name=self.session_name)
|
||||||
|
@ -231,7 +231,7 @@ def filter_tweet(tweet, tweet_data, settings, buffer_name):
|
|||||||
if word not in getattr(tweet, value):
|
if word not in getattr(tweet, value):
|
||||||
return False
|
return False
|
||||||
if settings["filters"][i]["in_lang"] == "True":
|
if settings["filters"][i]["in_lang"] == "True":
|
||||||
if getattr(tweet, lang) not in settings["filters"][i]["languages"]:
|
if getattr(tweet, "lang") not in settings["filters"][i]["languages"]:
|
||||||
return False
|
return False
|
||||||
elif settings["filters"][i]["in_lang"] == "False":
|
elif settings["filters"][i]["in_lang"] == "False":
|
||||||
if tweet.lang in settings["filters"][i]["languages"]:
|
if tweet.lang in settings["filters"][i]["languages"]:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
class authorisationDialog(wx.Dialog):
|
class authorisationDialog(wx.Dialog):
|
||||||
|
@ -111,7 +111,8 @@ class URLStream(object):
|
|||||||
self.prepared = False
|
self.prepared = False
|
||||||
log.debug("URL Player initialized")
|
log.debug("URL Player initialized")
|
||||||
# LibVLC controls.
|
# LibVLC controls.
|
||||||
self.instance = vlc.Instance()
|
self.instance = vlc.Instance("--quiet")
|
||||||
|
self.instance.log_unset()
|
||||||
self.player = self.instance.media_player_new()
|
self.player = self.instance.media_player_new()
|
||||||
self.event_manager = self.player.event_manager()
|
self.event_manager = self.player.event_manager()
|
||||||
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback)
|
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback)
|
||||||
|
1
src/test/sessions/__init__.py
Normal file
1
src/test/sessions/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
201
src/test/sessions/test_base_session.py
Normal file
201
src/test/sessions/test_base_session.py
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
import sqlitedict
|
||||||
|
import shutil
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
# Mock sound module, so LibVLc won't complain.
|
||||||
|
sound_module = types.ModuleType("sound")
|
||||||
|
sys.modules["sound"] = sound_module
|
||||||
|
sound_module.soundManager = mock.MagicMock(name="sound.soundManager")
|
||||||
|
from sessions import base
|
||||||
|
|
||||||
|
# path where we will save our test config, as we can't rely on paths module due to pytest's paths being different.
|
||||||
|
session_path = os.path.join(os.getcwd(), "config", "testing")
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def session():
|
||||||
|
""" Configures a fake base session from where we can test things. """
|
||||||
|
global session_path
|
||||||
|
s = base.baseSession("testing")
|
||||||
|
if os.path.exists(session_path) == False:
|
||||||
|
os.mkdir(session_path)
|
||||||
|
# Patches paths.app_path and paths.config_path, so we will not have issues during session configuration.
|
||||||
|
with mock.patch("paths.app_path", return_value=os.getcwd()) as app_path:
|
||||||
|
with mock.patch("paths.config_path", return_value=os.path.join(os.getcwd(), "config")) as config_path:
|
||||||
|
s.get_configuration()
|
||||||
|
yield s
|
||||||
|
# Session's cleanup code.
|
||||||
|
if os.path.exists(session_path):
|
||||||
|
shutil.rmtree(session_path)
|
||||||
|
del s
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dataset():
|
||||||
|
""" Generates a sample dataset"""
|
||||||
|
dataset = dict(home_timeline=["message" for i in range(10000)], mentions_timeline=["mention" for i in range(20000)])
|
||||||
|
yield dataset
|
||||||
|
|
||||||
|
### Testing database being read from disk.
|
||||||
|
def test_cache_in_disk_unlimited_size(session, dataset):
|
||||||
|
""" Tests cache database being read from disk, storing the whole datasets. """
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = False
|
||||||
|
session.settings["general"]["persist_size"] = -1
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
session.save_persistent_data()
|
||||||
|
assert isinstance(session.db, sqlitedict.SqliteDict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert len(session.db.get("home_timeline")) == 10000
|
||||||
|
assert len(session.db.get("mentions_timeline")) == 20000
|
||||||
|
session.db.close()
|
||||||
|
|
||||||
|
def test_cache_in_disk_limited_dataset(session, dataset):
|
||||||
|
""" Tests wether the cache stores only the amount of items we ask it to store. """
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = False
|
||||||
|
session.settings["general"]["persist_size"] = 100
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
# We need to save and load the db again because we cannot modify buffers' size while the database is opened.
|
||||||
|
# As TWBlue reads directly from db when reading from disk, an attempt to modify buffers size while Blue is reading the db
|
||||||
|
# Might cause an out of sync error between the GUI lists and the database.
|
||||||
|
# So we perform the changes to buffer size when loading data during app startup if the DB is read from disk.
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, sqlitedict.SqliteDict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert len(session.db.get("home_timeline")) == 100
|
||||||
|
assert len(session.db.get("mentions_timeline")) == 100
|
||||||
|
session.db.close()
|
||||||
|
|
||||||
|
def test_cache_in_disk_limited_dataset_unreversed(session):
|
||||||
|
"""Test if the cache is saved properly in unreversed buffers, when newest items are at the end of the list. """
|
||||||
|
dataset = dict(home_timeline=[i for i in range(20)], mentions_timeline=[i for i in range(20)])
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = False
|
||||||
|
session.settings["general"]["persist_size"] = 10
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
# We need to save and load the db again because we cannot modify buffers' size while the database is opened.
|
||||||
|
# As TWBlue reads directly from db when reading from disk, an attempt to modify buffers size while Blue is reading the db
|
||||||
|
# Might cause an out of sync error between the GUI lists and the database.
|
||||||
|
# So we perform the changes to buffer size when loading data during app startup if the DB is read from disk.
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, sqlitedict.SqliteDict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert session.db.get("home_timeline")[0] == 10
|
||||||
|
assert session.db.get("mentions_timeline")[0] == 10
|
||||||
|
assert session.db.get("home_timeline")[-1] == 19
|
||||||
|
assert session.db.get("mentions_timeline")[-1] == 19
|
||||||
|
session.db.close()
|
||||||
|
|
||||||
|
def test_cache_in_disk_limited_dataset_reversed(session):
|
||||||
|
"""Test if the cache is saved properly in reversed buffers, when newest items are at the start of the list. """
|
||||||
|
dataset = dict(home_timeline=[i for i in range(19, -1, -1)], mentions_timeline=[i for i in range(19, -1, -1)])
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = False
|
||||||
|
session.settings["general"]["persist_size"] = 10
|
||||||
|
session.settings["general"]["reverse_timelines"] = True
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
# We need to save and load the db again because we cannot modify buffers' size while the database is opened.
|
||||||
|
# As TWBlue reads directly from db when reading from disk, an attempt to modify buffers size while Blue is reading the db
|
||||||
|
# Might cause an out of sync error between the GUI lists and the database.
|
||||||
|
# So we perform the changes to buffer size when loading data during app startup if the DB is read from disk.
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, sqlitedict.SqliteDict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert session.db.get("home_timeline")[0] == 19
|
||||||
|
assert session.db.get("mentions_timeline")[0] == 19
|
||||||
|
assert session.db.get("home_timeline")[-1] == 10
|
||||||
|
assert session.db.get("mentions_timeline")[-1] == 10
|
||||||
|
session.db.close()
|
||||||
|
|
||||||
|
### Testing database being loaded into memory. Those tests should give the same results than before
|
||||||
|
### but as we have different code depending whether we load db into memory or read it from disk,
|
||||||
|
### We need to test this anyways.
|
||||||
|
def test_cache_in_memory_unlimited_size(session, dataset):
|
||||||
|
""" Tests cache database being loaded in memory, storing the whole datasets. """
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = True
|
||||||
|
session.settings["general"]["persist_size"] = -1
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, dict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert len(session.db.get("home_timeline")) == 10000
|
||||||
|
assert len(session.db.get("mentions_timeline")) == 20000
|
||||||
|
|
||||||
|
def test_cache_in_memory_limited_dataset(session, dataset):
|
||||||
|
""" Tests wether the cache stores only the amount of items we ask it to store, when loaded in memory. """
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = True
|
||||||
|
session.settings["general"]["persist_size"] = 100
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, dict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert len(session.db.get("home_timeline")) == 100
|
||||||
|
assert len(session.db.get("mentions_timeline")) == 100
|
||||||
|
|
||||||
|
def test_cache_in_memory_limited_dataset_unreversed(session):
|
||||||
|
"""Test if the cache is saved properly when loaded in memory in unreversed buffers, when newest items are at the end of the list. """
|
||||||
|
dataset = dict(home_timeline=[i for i in range(20)], mentions_timeline=[i for i in range(20)])
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = True
|
||||||
|
session.settings["general"]["persist_size"] = 10
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert len(session.db)==1
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, dict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert session.db.get("home_timeline")[0] == 10
|
||||||
|
assert session.db.get("mentions_timeline")[0] == 10
|
||||||
|
assert session.db.get("home_timeline")[-1] == 19
|
||||||
|
assert session.db.get("mentions_timeline")[-1] == 19
|
||||||
|
|
||||||
|
def test_cache_in_memory_limited_dataset_reversed(session):
|
||||||
|
"""Test if the cache is saved properly in reversed buffers, when newest items are at the start of the list. This test if for db read into memory. """
|
||||||
|
dataset = dict(home_timeline=[i for i in range(19, -1, -1)], mentions_timeline=[i for i in range(19, -1, -1)])
|
||||||
|
session.settings["general"]["load_cache_in_memory"] = True
|
||||||
|
session.settings["general"]["persist_size"] = 10
|
||||||
|
session.settings["general"]["reverse_timelines"] = True
|
||||||
|
session.load_persistent_data()
|
||||||
|
session.db["home_timeline"] = dataset["home_timeline"]
|
||||||
|
session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
||||||
|
session.save_persistent_data()
|
||||||
|
session.db = dict()
|
||||||
|
session.load_persistent_data()
|
||||||
|
assert isinstance(session.db, dict)
|
||||||
|
assert session.db.get("home_timeline") != None
|
||||||
|
assert session.db.get("mentions_timeline") != None
|
||||||
|
assert session.db.get("home_timeline")[0] == 19
|
||||||
|
assert session.db.get("mentions_timeline")[0] == 19
|
||||||
|
assert session.db.get("home_timeline")[-1] == 10
|
||||||
|
assert session.db.get("mentions_timeline")[-1] == 10
|
1
src/test/sessions/twitter/__init__.py
Normal file
1
src/test/sessions/twitter/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
13
src/test/sessions/twitter/conftest.py
Normal file
13
src/test/sessions/twitter/conftest.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
from tweepy.models import Status
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def basic_tweet():
|
||||||
|
data = {'created_at': 'Mon Jan 03 15:03:36 +0000 2022', 'id': 1478019218884857856, 'id_str': '1478019218884857856', 'full_text': 'Changes in projects for next year https://t.co/nW3GS9RmHd', 'truncated': False, 'display_text_range': [0, 57], 'entities': {'hashtags': [], 'symbols': [], 'user_mentions': [], 'urls': [{'url': 'https://t.co/nW3GS9RmHd', 'expanded_url': 'https://manuelcortez.net/blog/changes-in-projects-for-next-year/#.YdMQQU6t1FI.twitter', 'display_url': 'manuelcortez.net/blog/changes-i…', 'indices': [34, 57]}]}, 'source': '<a href="https://mobile.twitter.com" rel="nofollow">Twitter Web App</a>', 'in_reply_to_status_id': None, 'in_reply_to_status_id_str': None, 'in_reply_to_user_id': None, 'in_reply_to_user_id_str': None, 'in_reply_to_screen_name': None, 'user': {'id': 258677951, 'id_str': '258677951', 'name': 'Manuel Cortez', 'screen_name': 'manuelcortez00', 'location': 'Nuevo León, México', 'description': 'Python developer, , interested in reading, accessibility, astronomy, physics and science. Я учу русский.', 'url': 'https://t.co/JFRKRA73ZV', 'entities': {'url': {'urls': [{'url': 'https://t.co/JFRKRA73ZV', 'expanded_url': 'https://manuelcortez.net', 'display_url': 'manuelcortez.net', 'indices': [0, 23]}]}, 'description': {'urls': []}}, 'protected': False, 'followers_count': 1453, 'friends_count': 568, 'listed_count': 45, 'created_at': 'Mon Feb 28 06:52:48 +0000 2011', 'favourites_count': 283, 'utc_offset': None, 'time_zone': None, 'geo_enabled': True, 'verified': False, 'statuses_count': 43371, 'lang': None, 'contributors_enabled': False, 'is_translator': False, 'is_translation_enabled': False, 'profile_background_color': 'C0DEED', 'profile_background_image_url': 'http://abs.twimg.com/images/themes/theme1/bg.png', 'profile_background_image_url_https': 'https://abs.twimg.com/images/themes/theme1/bg.png', 'profile_background_tile': False, 'profile_image_url': 'http://pbs.twimg.com/profile_images/442466677645508608/3EBBC-OX_normal.jpeg', 'profile_image_url_https': 'https://pbs.twimg.com/profile_images/442466677645508608/3EBBC-OX_normal.jpeg', 'profile_image_extensions_alt_text': None, 'profile_link_color': '1DA1F2', 'profile_sidebar_border_color': 'C0DEED', 'profile_sidebar_fill_color': 'DDEEF6', 'profile_text_color': '333333', 'profile_use_background_image': True, 'has_extended_profile': False, 'default_profile': True, 'default_profile_image': False, 'following': False, 'follow_request_sent': False, 'notifications': False, 'translator_type': 'regular', 'withheld_in_countries': []}, 'geo': None, 'coordinates': None, 'place': None, 'contributors': None, 'is_quote_status': False, 'retweet_count': 6, 'favorite_count': 2, 'favorited': False, 'retweeted': False, 'possibly_sensitive': False, 'possibly_sensitive_appealable': False, 'lang': 'en'}
|
||||||
|
yield Status().parse(api=None, json=data)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def basic_tweet_multiple_mentions():
|
||||||
|
data = {'created_at': 'Mon Dec 27 21:21:25 +0000 2021', 'id': 1475577584947707909, 'id_str': '1475577584947707909', 'full_text': '@tamaranatalia9 @Darkstrings @Chris88171572 @manuelcortez00 Well done, thanks Tamara', 'truncated': False, 'display_text_range': [60, 84], 'entities': {'hashtags': [], 'symbols': [], 'user_mentions': [{'screen_name': 'tamaranatalia9', 'name': 'Tamara', 'id': 914114584591597568, 'id_str': '914114584591597568', 'indices': [0, 15]}, {'screen_name': 'Darkstrings', 'name': 'Luc', 'id': 1374154151115042823, 'id_str': '1374154151115042823', 'indices': [16, 28]}, {'screen_name': 'Chris88171572', 'name': 'Chris', 'id': 1323980014799495168, 'id_str': '1323980014799495168', 'indices': [29, 43]}, {'screen_name': 'manuelcortez00', 'name': 'Manuel Cortez', 'id': 258677951, 'id_str': '258677951', 'indices': [44, 59]}], 'urls': []}, 'source': '<a href="http://twitter.com/download/android" rel="nofollow">Twitter for Android</a>', 'in_reply_to_status_id': 1475550502083563526, 'in_reply_to_status_id_str': '1475550502083563526', 'in_reply_to_user_id': 914114584591597568, 'in_reply_to_user_id_str': '914114584591597568', 'in_reply_to_screen_name': 'tamaranatalia9', 'user': {'id': 784837522157436929, 'id_str': '784837522157436929', 'name': 'Paulus', 'screen_name': 'PauloPer01', 'location': '', 'description': '', 'url': None, 'entities': {'description': {'urls': []}}, 'protected': False, 'followers_count': 1082, 'friends_count': 3029, 'listed_count': 2, 'created_at': 'Sat Oct 08 19:27:01 +0000 2016', 'favourites_count': 78862, 'utc_offset': None, 'time_zone': None, 'geo_enabled': False, 'verified': False, 'statuses_count': 4976, 'lang': None, 'contributors_enabled': False, 'is_translator': False, 'is_translation_enabled': False, 'profile_background_color': 'F5F8FA', 'profile_background_image_url': None, 'profile_background_image_url_https': None, 'profile_background_tile': False, 'profile_image_url': 'http://pbs.twimg.com/profile_images/1464572633014587395/246oPPLa_normal.jpg', 'profile_image_url_https': 'https://pbs.twimg.com/profile_images/1464572633014587395/246oPPLa_normal.jpg', 'profile_image_extensions_alt_text': None, 'profile_link_color': '1DA1F2', 'profile_sidebar_border_color': 'C0DEED', 'profile_sidebar_fill_color': 'DDEEF6', 'profile_text_color': '333333', 'profile_use_background_image': True, 'has_extended_profile': True, 'default_profile': True, 'default_profile_image': False, 'following': False, 'follow_request_sent': False, 'notifications': False, 'translator_type': 'none', 'withheld_in_countries': []}, 'geo': None, 'coordinates': None, 'place': None, 'contributors': None, 'is_quote_status': False, 'retweet_count': 1, 'favorite_count': 2, 'favorited': False, 'retweeted': False, 'lang': 'en'}
|
||||||
|
yield Status().parse(api=None, json=data)
|
52
src/test/sessions/twitter/test_twitter_templates.py
Normal file
52
src/test/sessions/twitter/test_twitter_templates.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
import gettext
|
||||||
|
import datetime
|
||||||
|
gettext.install("test")
|
||||||
|
from unittest import mock
|
||||||
|
from sessions.twitter import templates
|
||||||
|
|
||||||
|
def test_default_values():
|
||||||
|
""" Tests wheter default values are the expected ones.
|
||||||
|
This might be useful so we will have this failing when we update anything from those values.
|
||||||
|
As TWBlue might be using those from other dialogs.
|
||||||
|
"""
|
||||||
|
assert templates.tweet_variables == ["date", "display_name", "screen_name", "source", "lang", "text", "image_descriptions"]
|
||||||
|
assert templates.dm_variables == ["date", "sender_display_name", "sender_screen_name", "recipient_display_name", "recipient_display_name", "text"]
|
||||||
|
assert templates.person_variables == ["display_name", "screen_name", "location", "description", "followers", "following", "listed", "likes", "tweets", "created_at"]
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("offset, language, expected_result", [
|
||||||
|
(0, "en_US", "Wednesday, October 10, 2018 20:19:24"),
|
||||||
|
(-21600, "en_US", "Wednesday, October 10, 2018 14:19:24"),
|
||||||
|
(7200, "en_US", "Wednesday, October 10, 2018 22:19:24"),
|
||||||
|
(0, "es_ES", "miércoles, octubre 10, 2018 20:19:24"),
|
||||||
|
(-21600, "es_ES", "miércoles, octubre 10, 2018 14:19:24"),
|
||||||
|
(7200, "es_ES", "miércoles, octubre 10, 2018 22:19:24"),
|
||||||
|
(18000, "es_ES", "jueves, octubre 11, 2018 1:19:24"),
|
||||||
|
])
|
||||||
|
def test_process_date_absolute_time(offset, language, expected_result):
|
||||||
|
""" Tests date processing function for tweets, when relative_times is set to False. """
|
||||||
|
# Date representation used by twitter, converted to datetime object, as tweepy already does this.
|
||||||
|
# Original date was Wed Oct 10 20:19:24 +0000 2018
|
||||||
|
date_field = datetime.datetime(2018, 10, 10, 20, 19, 24)
|
||||||
|
with mock.patch("languageHandler.curLang", new=language):
|
||||||
|
processed_date = templates.process_date(date_field, relative_times=False, offset_seconds=offset)
|
||||||
|
assert processed_date == expected_result
|
||||||
|
|
||||||
|
def test_process_date_relative_time():
|
||||||
|
date_field = datetime.datetime(2018, 10, 10, 20, 19, 24)
|
||||||
|
with mock.patch("languageHandler.curLang", new="es_ES"):
|
||||||
|
processed_date = templates.process_date(date_field, relative_times=True, offset_seconds=7200)
|
||||||
|
# As this depends in relative times and this is subject to change, let's do some light checks here and hope the string is going to be valid.
|
||||||
|
assert isinstance(processed_date, str)
|
||||||
|
assert "hace" in processed_date and "años" in processed_date
|
||||||
|
|
||||||
|
def test_process_text_basic_tweet(basic_tweet):
|
||||||
|
expected_result = "Changes in projects for next year https://manuelcortez.net/blog/changes-in-projects-for-next-year/#.YdMQQU6t1FI.twitter"
|
||||||
|
text = templates.process_text(basic_tweet)
|
||||||
|
assert text == expected_result
|
||||||
|
|
||||||
|
def test_process_text_basic_tweet_multiple_mentions(basic_tweet_multiple_mentions):
|
||||||
|
expected_result = "@tamaranatalia9, @Darkstrings and 2 more: Well done, thanks Tamara"
|
||||||
|
text = templates.process_text(basic_tweet_multiple_mentions)
|
||||||
|
assert text == expected_result
|
@ -1,200 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Test case to check some of the scenarios we might face when storing tweets in cache, both loading into memory or rreading from disk. """
|
|
||||||
import unittest
|
|
||||||
import os
|
|
||||||
import paths
|
|
||||||
import sqlitedict
|
|
||||||
import shutil
|
|
||||||
# The base session module requires sound as a dependency, and this needs libVLC to be locatable.
|
|
||||||
os.environ['PYTHON_VLC_MODULE_PATH']=os.path.abspath(os.path.join(paths.app_path(), "..", "windows-dependencies", "x86"))
|
|
||||||
os.environ['PYTHON_VLC_LIB_PATH']=os.path.abspath(os.path.join(paths.app_path(), "..", "windows-dependencies", "x86", "libvlc.dll"))
|
|
||||||
from sessions import base
|
|
||||||
|
|
||||||
class cacheTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
""" Configures a fake session to check caching objects here. """
|
|
||||||
self.session = base.baseSession("testing")
|
|
||||||
if os.path.exists(os.path.join(paths.config_path(), "testing")) == False:
|
|
||||||
os.mkdir(os.path.join(paths.config_path(), "testing"))
|
|
||||||
self.session.get_configuration()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
""" Removes the previously configured session. """
|
|
||||||
session_folder = os.path.join(paths.config_path(), "testing")
|
|
||||||
if os.path.exists(session_folder):
|
|
||||||
shutil.rmtree(session_folder)
|
|
||||||
|
|
||||||
def generate_dataset(self):
|
|
||||||
""" Generates a sample dataset"""
|
|
||||||
dataset = dict(home_timeline=["message" for i in range(10000)], mentions_timeline=["mention" for i in range(20000)])
|
|
||||||
return dataset
|
|
||||||
|
|
||||||
### Testing database being read from disk.
|
|
||||||
|
|
||||||
def test_cache_in_disk_unlimited_size(self):
|
|
||||||
""" Tests cache database being read from disk, storing the whole datasets. """
|
|
||||||
dataset = self.generate_dataset()
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = False
|
|
||||||
self.session.settings["general"]["persist_size"] = -1
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, sqlitedict.SqliteDict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(len(self.session.db.get("home_timeline")), 10000)
|
|
||||||
self.assertEquals(len(self.session.db.get("mentions_timeline")), 20000)
|
|
||||||
self.session.db.close()
|
|
||||||
|
|
||||||
def test_cache_in_disk_limited_dataset(self):
|
|
||||||
""" Tests wether the cache stores only the amount of items we ask it to store. """
|
|
||||||
dataset = self.generate_dataset()
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = False
|
|
||||||
self.session.settings["general"]["persist_size"] = 100
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
# We need to save and load the db again because we cannot modify buffers' size while the database is opened.
|
|
||||||
# As TWBlue reads directly from db when reading from disk, an attempt to modify buffers size while Blue is reading the db
|
|
||||||
# Might cause an out of sync error between the GUI lists and the database.
|
|
||||||
# So we perform the changes to buffer size when loading data during app startup if the DB is read from disk.
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, sqlitedict.SqliteDict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(len(self.session.db.get("home_timeline")), 100)
|
|
||||||
self.assertEquals(len(self.session.db.get("mentions_timeline")), 100)
|
|
||||||
self.session.db.close()
|
|
||||||
|
|
||||||
def test_cache_in_disk_limited_dataset_unreversed(self):
|
|
||||||
"""Test if the cache is saved properly in unreversed buffers, when newest items are at the end of the list. """
|
|
||||||
dataset = dict(home_timeline=[i for i in range(20)], mentions_timeline=[i for i in range(20)])
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = False
|
|
||||||
self.session.settings["general"]["persist_size"] = 10
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
# We need to save and load the db again because we cannot modify buffers' size while the database is opened.
|
|
||||||
# As TWBlue reads directly from db when reading from disk, an attempt to modify buffers size while Blue is reading the db
|
|
||||||
# Might cause an out of sync error between the GUI lists and the database.
|
|
||||||
# So we perform the changes to buffer size when loading data during app startup if the DB is read from disk.
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, sqlitedict.SqliteDict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[0], 10)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[0], 10)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[-1], 19)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[-1], 19)
|
|
||||||
self.session.db.close()
|
|
||||||
|
|
||||||
def test_cache_in_disk_limited_dataset_reversed(self):
|
|
||||||
"""Test if the cache is saved properly in reversed buffers, when newest items are at the start of the list. """
|
|
||||||
dataset = dict(home_timeline=[i for i in range(19, -1, -1)], mentions_timeline=[i for i in range(19, -1, -1)])
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = False
|
|
||||||
self.session.settings["general"]["persist_size"] = 10
|
|
||||||
self.session.settings["general"]["reverse_timelines"] = True
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
# We need to save and load the db again because we cannot modify buffers' size while the database is opened.
|
|
||||||
# As TWBlue reads directly from db when reading from disk, an attempt to modify buffers size while Blue is reading the db
|
|
||||||
# Might cause an out of sync error between the GUI lists and the database.
|
|
||||||
# So we perform the changes to buffer size when loading data during app startup if the DB is read from disk.
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, sqlitedict.SqliteDict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[0], 19)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[0], 19)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[-1], 10)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[-1], 10)
|
|
||||||
self.session.db.close()
|
|
||||||
|
|
||||||
### Testing database being loaded into memory. Those tests should give the same results than before
|
|
||||||
### but as we have different code depending whether we load db into memory or read it from disk,
|
|
||||||
### We need to test this anyways.
|
|
||||||
def test_cache_in_memory_unlimited_size(self):
|
|
||||||
""" Tests cache database being loaded in memory, storing the whole datasets. """
|
|
||||||
dataset = self.generate_dataset()
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = True
|
|
||||||
self.session.settings["general"]["persist_size"] = -1
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, dict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(len(self.session.db.get("home_timeline")), 10000)
|
|
||||||
self.assertEquals(len(self.session.db.get("mentions_timeline")), 20000)
|
|
||||||
|
|
||||||
def test_cache_in_memory_limited_dataset(self):
|
|
||||||
""" Tests wether the cache stores only the amount of items we ask it to store, when loaded in memory. """
|
|
||||||
dataset = self.generate_dataset()
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = True
|
|
||||||
self.session.settings["general"]["persist_size"] = 100
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, dict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(len(self.session.db.get("home_timeline")), 100)
|
|
||||||
self.assertEquals(len(self.session.db.get("mentions_timeline")), 100)
|
|
||||||
|
|
||||||
def test_cache_in_memory_limited_dataset_unreversed(self):
|
|
||||||
"""Test if the cache is saved properly when loaded in memory in unreversed buffers, when newest items are at the end of the list. """
|
|
||||||
dataset = dict(home_timeline=[i for i in range(20)], mentions_timeline=[i for i in range(20)])
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = True
|
|
||||||
self.session.settings["general"]["persist_size"] = 10
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertTrue(len(self.session.db)==1)
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, dict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[0], 10)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[0], 10)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[-1], 19)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[-1], 19)
|
|
||||||
|
|
||||||
def test_cache_in_memory_limited_dataset_reversed(self):
|
|
||||||
"""Test if the cache is saved properly in reversed buffers, when newest items are at the start of the list. This test if for db read into memory. """
|
|
||||||
dataset = dict(home_timeline=[i for i in range(19, -1, -1)], mentions_timeline=[i for i in range(19, -1, -1)])
|
|
||||||
self.session.settings["general"]["load_cache_in_memory"] = True
|
|
||||||
self.session.settings["general"]["persist_size"] = 10
|
|
||||||
self.session.settings["general"]["reverse_timelines"] = True
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.session.db["home_timeline"] = dataset["home_timeline"]
|
|
||||||
self.session.db["mentions_timeline"] = dataset["mentions_timeline"]
|
|
||||||
self.session.save_persistent_data()
|
|
||||||
self.session.db = dict()
|
|
||||||
self.session.load_persistent_data()
|
|
||||||
self.assertIsInstance(self.session.db, dict)
|
|
||||||
self.assertTrue(self.session.db.get("home_timeline") != None)
|
|
||||||
self.assertTrue(self.session.db.get("mentions_timeline") != None)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[0], 19)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[0], 19)
|
|
||||||
self.assertEquals(self.session.db.get("home_timeline")[-1], 10)
|
|
||||||
self.assertEquals(self.session.db.get("mentions_timeline")[-1], 10)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
@ -1,13 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
from . import twitter, mastodon
|
||||||
from __future__ import unicode_literals
|
|
||||||
from .base import basePanel
|
|
||||||
from .dm import dmPanel
|
|
||||||
from .events import eventsPanel
|
|
||||||
from .favourites import favsPanel
|
|
||||||
from .lists import listPanel
|
|
||||||
from .panels import accountPanel, emptyPanel
|
from .panels import accountPanel, emptyPanel
|
||||||
from .people import peoplePanel
|
|
||||||
from .trends import trendsPanel
|
|
||||||
from .tweet_searches import searchPanel
|
|
||||||
from .user_searches import searchUsersPanel
|
|
||||||
|
4
src/wxUI/buffers/mastodon/__init__.py
Normal file
4
src/wxUI/buffers/mastodon/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from .base import basePanel
|
||||||
|
from .conversationList import conversationListPanel
|
||||||
|
from .user import userPanel
|
49
src/wxUI/buffers/mastodon/base.py
Normal file
49
src/wxUI/buffers/mastodon/base.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
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.post = wx.Button(self, -1, _("Post"))
|
||||||
|
self.boost = wx.Button(self, -1, _("Boost"))
|
||||||
|
self.reply = wx.Button(self, -1, _(u"Reply"))
|
||||||
|
self.fav = wx.Button(self, wx.ID_ANY, _("Favorite"))
|
||||||
|
self.bookmark = wx.Button(self, wx.ID_ANY, _("Bookmark"))
|
||||||
|
self.dm = wx.Button(self, -1, _(u"Direct message"))
|
||||||
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
btnSizer.Add(self.post, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.boost, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.reply, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.fav, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.bookmark, 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()
|
41
src/wxUI/buffers/mastodon/conversationList.py
Normal file
41
src/wxUI/buffers/mastodon/conversationList.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
from multiplatform_widgets import widgets
|
||||||
|
|
||||||
|
class conversationListPanel(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(conversationListPanel, self).__init__(parent)
|
||||||
|
self.name = name
|
||||||
|
self.type = "baseBuffer"
|
||||||
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.create_list()
|
||||||
|
self.post = wx.Button(self, -1, _("Post"))
|
||||||
|
self.reply = wx.Button(self, -1, _(u"Reply"))
|
||||||
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
btnSizer.Add(self.post, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.reply, 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()
|
37
src/wxUI/buffers/mastodon/user.py
Normal file
37
src/wxUI/buffers/mastodon/user.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
from multiplatform_widgets import widgets
|
||||||
|
|
||||||
|
class userPanel(wx.Panel):
|
||||||
|
|
||||||
|
def create_list(self):
|
||||||
|
self.list = widgets.list(self, _("User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
|
||||||
|
self.list.set_windows_size(0, 320)
|
||||||
|
self.list.set_size()
|
||||||
|
|
||||||
|
def __init__(self, parent, name):
|
||||||
|
super(userPanel, self).__init__(parent)
|
||||||
|
self.name = name
|
||||||
|
self.type = "user"
|
||||||
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.create_list()
|
||||||
|
self.post = wx.Button(self, -1, _("Post"))
|
||||||
|
self.actions = wx.Button(self, -1, _("Actions"))
|
||||||
|
self.message = wx.Button(self, -1, _("Message"))
|
||||||
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
btnSizer.Add(self.post, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.actions, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.message, 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()
|
10
src/wxUI/buffers/twitter/__init__.py
Normal file
10
src/wxUI/buffers/twitter/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# -*- 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
|
0
src/wxUI/dialogs/mastodon/__init__.py
Normal file
0
src/wxUI/dialogs/mastodon/__init__.py
Normal file
47
src/wxUI/dialogs/mastodon/dialogs.py
Normal file
47
src/wxUI/dialogs/mastodon/dialogs.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
import application
|
||||||
|
|
||||||
|
def boost_question():
|
||||||
|
result = False
|
||||||
|
dlg = wx.MessageDialog(None, _("Would you like to share this post?"), _("Boost"), wx.YES_NO|wx.ICON_QUESTION)
|
||||||
|
if dlg.ShowModal() == wx.ID_YES:
|
||||||
|
result = True
|
||||||
|
dlg.Destroy()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def delete_post_dialog():
|
||||||
|
result = False
|
||||||
|
dlg = wx.MessageDialog(None, _("Do you really want to delete this post? It will be deleted from the instance as well."), _("Delete"), wx.ICON_QUESTION|wx.YES_NO)
|
||||||
|
if dlg.ShowModal() == wx.ID_YES:
|
||||||
|
result = True
|
||||||
|
dlg.Destroy()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def clear_list():
|
||||||
|
result = False
|
||||||
|
dlg = wx.MessageDialog(None, _("Do you really want to empty this buffer? It's items will be removed from the list but not from the instance"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
|
||||||
|
if dlg.ShowModal() == wx.ID_YES:
|
||||||
|
result = True
|
||||||
|
dlg.Destroy()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def no_posts():
|
||||||
|
dlg = wx.MessageDialog(None, _("This user has no posts. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR)
|
||||||
|
dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def no_favs():
|
||||||
|
dlg = wx.MessageDialog(None, _(u"This user has no favorited posts. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR)
|
||||||
|
dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def no_followers():
|
||||||
|
dlg = wx.MessageDialog(None, _(u"This user has no followers yet. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR)
|
||||||
|
dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def no_following():
|
||||||
|
dlg = wx.MessageDialog(None, _("This user is not following anyone. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR)
|
||||||
|
dlg.ShowModal()
|
||||||
|
dlg.Destroy()
|
28
src/wxUI/dialogs/mastodon/menus.py
Normal file
28
src/wxUI/dialogs/mastodon/menus.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class base(wx.Menu):
|
||||||
|
def __init__(self):
|
||||||
|
super(base, self).__init__()
|
||||||
|
self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost"))
|
||||||
|
self.Append(self.boost)
|
||||||
|
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
||||||
|
self.Append(self.reply)
|
||||||
|
self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
|
||||||
|
self.Append(self.fav)
|
||||||
|
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
|
||||||
|
self.Append(self.unfav)
|
||||||
|
self.openUrl = wx.MenuItem(self, wx.ID_ANY, _("&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)
|
328
src/wxUI/dialogs/mastodon/postDialogs.py
Normal file
328
src/wxUI/dialogs/mastodon/postDialogs.py
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
import wx
|
||||||
|
|
||||||
|
class Post(wx.Dialog):
|
||||||
|
def __init__(self, caption=_("Post"), text="", *args, **kwds):
|
||||||
|
super(Post, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwds)
|
||||||
|
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
post_sizer = wx.WrapSizer(wx.VERTICAL)
|
||||||
|
main_sizer.Add(post_sizer, 1, wx.EXPAND, 0)
|
||||||
|
post_label = wx.StaticText(self, wx.ID_ANY, caption)
|
||||||
|
post_sizer.Add(post_label, 0, 0, 0)
|
||||||
|
self.text = wx.TextCtrl(self, wx.ID_ANY, text, style=wx.TE_MULTILINE)
|
||||||
|
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
||||||
|
self.text.SetMinSize((350, -1))
|
||||||
|
post_sizer.Add(self.text, 0, 0, 0)
|
||||||
|
lists_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
main_sizer.Add(lists_sizer, 1, wx.EXPAND, 0)
|
||||||
|
attachments_sizer = wx.WrapSizer(wx.VERTICAL)
|
||||||
|
lists_sizer.Add(attachments_sizer, 1, wx.EXPAND, 0)
|
||||||
|
attachments_label = wx.StaticText(self, wx.ID_ANY, _("Attachments"))
|
||||||
|
attachments_sizer.Add(attachments_label, 0, 0, 0)
|
||||||
|
self.attachments = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
||||||
|
self.attachments.Enable(False)
|
||||||
|
self.attachments.AppendColumn(_("File"), format=wx.LIST_FORMAT_LEFT, width=-1)
|
||||||
|
self.attachments.AppendColumn(_("Type"), format=wx.LIST_FORMAT_LEFT, width=-1)
|
||||||
|
self.attachments.AppendColumn(_("Description"), format=wx.LIST_FORMAT_LEFT, width=-1)
|
||||||
|
attachments_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
||||||
|
self.remove_attachment = wx.Button(self, wx.ID_ANY, _("Remove Attachment"))
|
||||||
|
self.remove_attachment.Enable(False)
|
||||||
|
attachments_sizer.Add(self.remove_attachment, 0, 0, 0)
|
||||||
|
posts_sizer = wx.WrapSizer(wx.VERTICAL)
|
||||||
|
lists_sizer.Add(posts_sizer, 1, wx.EXPAND, 0)
|
||||||
|
posts_label = wx.StaticText(self, wx.ID_ANY, _("Post in the thread"))
|
||||||
|
posts_sizer.Add(posts_label, 0, 0, 0)
|
||||||
|
self.posts = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
||||||
|
self.posts.Enable(False)
|
||||||
|
self.posts.AppendColumn(_("Text"), format=wx.LIST_FORMAT_LEFT, width=-1)
|
||||||
|
self.posts.AppendColumn(_("Attachments"), format=wx.LIST_FORMAT_LEFT, width=-1)
|
||||||
|
posts_sizer.Add(self.posts, 1, wx.EXPAND, 0)
|
||||||
|
self.remove_post = wx.Button(self, wx.ID_ANY, _("Remove post"))
|
||||||
|
self.remove_post.Enable(False)
|
||||||
|
posts_sizer.Add(self.remove_post, 0, 0, 0)
|
||||||
|
post_actions_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
main_sizer.Add(post_actions_sizer, 1, wx.EXPAND, 0)
|
||||||
|
visibility_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
post_actions_sizer.Add(visibility_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_1 = wx.StaticText(self, wx.ID_ANY, _("Visibility"))
|
||||||
|
visibility_sizer.Add(label_1, 0, 0, 0)
|
||||||
|
self.visibility = wx.ComboBox(self, wx.ID_ANY, choices=[_("Public"), _("Not listed"), _("Followers only"), _("Direct")], style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SIMPLE)
|
||||||
|
self.visibility.SetSelection(0)
|
||||||
|
visibility_sizer.Add(self.visibility, 0, 0, 0)
|
||||||
|
self.add = wx.Button(self, wx.ID_ANY, _("A&dd"))
|
||||||
|
self.sensitive = wx.CheckBox(self, wx.ID_ANY, _("Sensitive content"))
|
||||||
|
self.sensitive.SetValue(False)
|
||||||
|
self.sensitive.Bind(wx.EVT_CHECKBOX, self.on_sensitivity_changed)
|
||||||
|
main_sizer.Add(self.sensitive, 0, wx.ALL, 5)
|
||||||
|
spoiler_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
spoiler_label = wx.StaticText(self, wx.ID_ANY, _("Content warning"))
|
||||||
|
self.spoiler = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
|
self.spoiler.Enable(False)
|
||||||
|
spoiler_box.Add(spoiler_label, 0, wx.ALL, 5)
|
||||||
|
spoiler_box.Add(self.spoiler, 0, wx.ALL, 10)
|
||||||
|
main_sizer.Add(spoiler_box, 0, wx.ALL, 5)
|
||||||
|
post_actions_sizer.Add(self.add, 0, 0, 0)
|
||||||
|
self.add_post = wx.Button(self, wx.ID_ANY, _("Add p&ost"))
|
||||||
|
post_actions_sizer.Add(self.add_post, 0, 0, 0)
|
||||||
|
text_actions_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
main_sizer.Add(text_actions_sizer, 1, wx.EXPAND, 0)
|
||||||
|
self.autocomplete_users = wx.Button(self, wx.ID_ANY, _("Auto&complete users"))
|
||||||
|
text_actions_sizer.Add(self.autocomplete_users, 0, 0, 0)
|
||||||
|
self.spellcheck = wx.Button(self, wx.ID_ANY, _("Check &spelling"))
|
||||||
|
text_actions_sizer.Add(self.spellcheck, 0, 0, 0)
|
||||||
|
self.translate = wx.Button(self, wx.ID_ANY, _("&Translate"))
|
||||||
|
text_actions_sizer.Add(self.translate, 0, 0, 0)
|
||||||
|
btn_sizer = wx.StdDialogButtonSizer()
|
||||||
|
main_sizer.Add(btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
||||||
|
self.send = wx.Button(self, wx.ID_OK, "")
|
||||||
|
self.send.SetDefault()
|
||||||
|
btn_sizer.AddButton(self.send)
|
||||||
|
self.close = wx.Button(self, wx.ID_CLOSE, "")
|
||||||
|
btn_sizer.AddButton(self.close)
|
||||||
|
btn_sizer.Realize()
|
||||||
|
self.SetSizer(main_sizer)
|
||||||
|
main_sizer.Fit(self)
|
||||||
|
self.SetEscapeId(self.close.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 on_sensitivity_changed(self, *args, **kwargs):
|
||||||
|
self.spoiler.Enable(self.sensitive.GetValue())
|
||||||
|
|
||||||
|
def set_title(self, chars):
|
||||||
|
self.SetTitle(_("Post - {} characters").format(chars))
|
||||||
|
|
||||||
|
def reset_controls(self):
|
||||||
|
self.text.ChangeValue("")
|
||||||
|
self.attachments.DeleteAllItems()
|
||||||
|
|
||||||
|
def add_item(self, list_type="attachment", item=[]):
|
||||||
|
if list_type == "attachment":
|
||||||
|
self.attachments.Append(item)
|
||||||
|
else:
|
||||||
|
self.posts.Append(item)
|
||||||
|
|
||||||
|
def remove_item(self, list_type="attachment"):
|
||||||
|
if list_type == "attachment":
|
||||||
|
item = self.attachments.GetFocusedItem()
|
||||||
|
if item > -1:
|
||||||
|
self.attachments.DeleteItem(item)
|
||||||
|
else:
|
||||||
|
item = self.posts.GetFocusedItem()
|
||||||
|
if item > -1:
|
||||||
|
self.posts.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_audio = menu.Append(wx.ID_ANY, _("Audio"))
|
||||||
|
self.add_audio.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, *.mov, *.m4v, *.webm)| *.mp4; *.m4v; *.mov; *.webm"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||||
|
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return None
|
||||||
|
return openFileDialog.GetPath()
|
||||||
|
|
||||||
|
def get_audio(self):
|
||||||
|
openFileDialog = wx.FileDialog(self, _("Select the audio file to be uploaded"), "", "", _("Audio files (*.mp3, *.ogg, *.wav, *.flac, *.opus, *.aac, *.m4a, *.3gp)|*.mp3; *.ogg; *.wav; *.flac; *.opus; *.aac; *.m4a; *.3gp"), 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 take into account that You can add only a maximum of 4 images, or one audio, video or poll per post. Please remove other attachments before continuing."), _("Error adding attachment"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
def unable_to_attach_poll(self, *args, **kwargs):
|
||||||
|
return wx.MessageDialog(self, _("You can add a poll or media files. In order to add your poll, please remove other attachments first."), _("Error adding poll"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
class viewPost(wx.Dialog):
|
||||||
|
def set_title(self, lenght):
|
||||||
|
self.SetTitle(_("Post - %i characters ") % (lenght,))
|
||||||
|
|
||||||
|
def __init__(self, text="", boosts_count=0, favs_count=0, source="", date="", privacy="", *args, **kwargs):
|
||||||
|
super(viewPost, self).__init__(parent=None, id=wx.ID_ANY, size=(850,850))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
label = wx.StaticText(panel, -1, _("Post"))
|
||||||
|
self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
|
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, _("Image description"))
|
||||||
|
self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
|
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)
|
||||||
|
privacyLabel = wx.StaticText(panel, -1, _("Privacy"))
|
||||||
|
privacy = wx.TextCtrl(panel, -1, privacy, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
privacyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
privacyBox.Add(privacyLabel, 0, wx.ALL, 5)
|
||||||
|
privacyBox.Add(privacy, 0, wx.ALL, 5)
|
||||||
|
boostsCountLabel = wx.StaticText(panel, -1, _(u"Boosts: "))
|
||||||
|
boostsCount = wx.TextCtrl(panel, -1, str(boosts_count), size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
boostBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
boostBox.Add(boostsCountLabel, 0, wx.ALL, 5)
|
||||||
|
boostBox.Add(boostsCount, 0, wx.ALL, 5)
|
||||||
|
favsCountLabel = wx.StaticText(panel, -1, _("Favorites: "))
|
||||||
|
favsCount = wx.TextCtrl(panel, -1, str(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, _("Source: "))
|
||||||
|
source = 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(source, 0, wx.ALL, 5)
|
||||||
|
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
||||||
|
date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
||||||
|
dateBox.Add(date, 0, wx.ALL, 5)
|
||||||
|
infoBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
infoBox.Add(privacyBox, 0, wx.ALL, 5)
|
||||||
|
infoBox.Add(boostBox, 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 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 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"))
|
||||||
|
period_sizer.Add(label_period, 0, 0, 0)
|
||||||
|
self.period = wx.ComboBox(self, wx.ID_ANY, choices=[_("5 minutes"), _("30 minutes"), _("1 hour"), _("6 hours"), _("1 day"), _("2 days"), _("3 days"), _("4 days"), _("5 days"), _("6 days"), _("7 days")], style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SIMPLE)
|
||||||
|
self.period.SetFocus()
|
||||||
|
self.period.SetSelection(0)
|
||||||
|
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)
|
||||||
|
self.multiple = wx.CheckBox(self, wx.ID_ANY, _("Allow multiple votes per user"))
|
||||||
|
self.multiple.SetValue(False)
|
||||||
|
sizer_1.Add(self.multiple, 0, wx.ALL, 5)
|
||||||
|
self.hide_votes = wx.CheckBox(self, wx.ID_ANY, _("Hide votes count until the poll expires"))
|
||||||
|
self.hide_votes.SetValue(False)
|
||||||
|
sizer_1.Add(self.hide_votes, 0, wx.ALL, 5)
|
||||||
|
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)
|
33
src/wxUI/dialogs/mastodon/search.py
Normal file
33
src/wxUI/dialogs/mastodon/search.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class searchDialog(wx.Dialog):
|
||||||
|
def __init__(self, value=""):
|
||||||
|
super(searchDialog, self).__init__(parent=None, id=wx.ID_ANY)
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.SetTitle(_("Search"))
|
||||||
|
label = wx.StaticText(panel, wx.ID_ANY, _("&Search"))
|
||||||
|
self.term = wx.TextCtrl(panel, wx.ID_ANY, 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.posts = wx.RadioButton(panel, wx.ID_ANY, _("Posts"), style=wx.RB_GROUP)
|
||||||
|
self.users = wx.RadioButton(panel, wx.ID_ANY, _("Users"))
|
||||||
|
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
radioSizer.Add(self.posts, 0, wx.ALL, 5)
|
||||||
|
radioSizer.Add(self.users, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
||||||
|
ok = wx.Button(panel, wx.ID_OK, _(u"&OK"))
|
||||||
|
ok.SetDefault()
|
||||||
|
cancel = wx.Button(panel, wx.ID_CANCEL, _("&Close"))
|
||||||
|
self.SetEscapeId(cancel.GetId())
|
||||||
|
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())
|
79
src/wxUI/dialogs/mastodon/userActions.py
Normal file
79
src/wxUI/dialogs/mastodon/userActions.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
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.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)
|
||||||
|
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.block.GetValue() == True: return "block"
|
||||||
|
elif self.unblock.GetValue() == True: return "unblock"
|
||||||
|
|
||||||
|
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 == "block":
|
||||||
|
self.block.SetValue(True)
|
||||||
|
elif default == "unblock":
|
||||||
|
self.unblock.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())
|
57
src/wxUI/dialogs/mastodon/userTimeline.py
Normal file
57
src/wxUI/dialogs/mastodon/userTimeline.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class UserTimeline(wx.Dialog):
|
||||||
|
def __init__(self, users=[], default="posts", *args, **kwargs):
|
||||||
|
super(UserTimeline, self).__init__(parent=None, *args, **kwargs)
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
userSizer = wx.BoxSizer()
|
||||||
|
self.SetTitle(_("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.posts = wx.RadioButton(panel, -1, _(u"&Posts"), style=wx.RB_GROUP)
|
||||||
|
self.followers = wx.RadioButton(panel, -1, _(u"&Followers"))
|
||||||
|
self.following = wx.RadioButton(panel, -1, _("Fo&llowing"))
|
||||||
|
self.setup_default(default)
|
||||||
|
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
hSizer.Add(label2, 0, wx.ALL, 5)
|
||||||
|
actionSizer.Add(self.posts, 0, wx.ALL, 5)
|
||||||
|
actionSizer.Add(self.followers, 0, wx.ALL, 5)
|
||||||
|
actionSizer.Add(self.following, 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.posts.GetValue() == True: return "posts"
|
||||||
|
elif self.followers.GetValue() == True: return "followers"
|
||||||
|
elif self.following.GetValue() == True: return "following"
|
||||||
|
|
||||||
|
def setup_default(self, default):
|
||||||
|
if default == "posts":
|
||||||
|
self.posts.SetValue(True)
|
||||||
|
|
||||||
|
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())
|
Loading…
Reference in New Issue
Block a user