mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-28 10:49:22 +00:00
Compare commits
82 Commits
v2022.02.2
...
v2022.8.28
Author | SHA1 | Date | |
---|---|---|---|
8116d98691 | |||
1b498a99bd | |||
1a4e594549 | |||
![]() |
2b0172f02f | ||
2d2c84fefe | |||
![]() |
0bef872070 | ||
b233842f5c | |||
0202b704da | |||
7d3ac47d55 | |||
e2ba61701f | |||
![]() |
8032f5cf75 | ||
![]() |
96f5d0e426 | ||
0ba71c985d | |||
c6cbe3360b | |||
ac80e039b2 | |||
2039597098 | |||
74fd4bfadf | |||
c374663b7c | |||
0f3d7d8747 | |||
0a395af3a9 | |||
![]() |
f181c5d1cd | ||
![]() |
204b9ebc02 | ||
0a479b55ed | |||
![]() |
eb0c3a650a | ||
829506acf5 | |||
14ee87b25a | |||
ebdf2e21bb | |||
![]() |
30a944fc61 | ||
![]() |
5c0af2fc01 | ||
e470498e56 | |||
fa2d2d9d78 | |||
b9794806d7 | |||
1e686cffba | |||
0f624c7dbe | |||
76a5c960e5 | |||
aab8aafefc | |||
015cf9eca3 | |||
769436abf7 | |||
b2da25dd61 | |||
03f59064d5 | |||
9afd6774f2 | |||
a1929ff1d3 | |||
4b627a13ff | |||
f9f7a32f90 | |||
f01ad5abb7 | |||
6fcd0274a9 | |||
654b34c8e1 | |||
02e1793d08 | |||
6e80b320c9 | |||
83c9db573e | |||
3aee5e8900 | |||
![]() |
a88af08181 | ||
79098cc730 | |||
d12e3aa335 | |||
![]() |
1d8fcf9166 | ||
3702405f8c | |||
7ce55c427e | |||
508b798bee | |||
53ac43a8b2 | |||
![]() |
22b8d91612 | ||
![]() |
4f619759e6 | ||
![]() |
0ac6f49379 | ||
![]() |
b332902b91 | ||
a138b8c02e | |||
c89dff053d | |||
018095752b | |||
bd90902e3b | |||
8bf9c6519e | |||
bed8e26204 | |||
fa9ebea836 | |||
be8d82a65f | |||
ab979e2623 | |||
20ad268e5a | |||
![]() |
83c9a7f0f9 | ||
9d90d91cfd | |||
bbf1356c89 | |||
67d15d8fe1 | |||
![]() |
3bd274db0c | ||
6147ca8658 | |||
![]() |
7833522b56 | ||
5e47e30bc3 | |||
![]() |
c6a2ba6a31 |
@@ -1,6 +1,22 @@
|
||||
TWBlue Changelog
|
||||
|
||||
## changes in this version
|
||||
|
||||
* the user autocompletion feature has been completely rewritten to be easier to use, particularly for people with many followers/following users:
|
||||
* In the account settings dialog, there's a button that opens up a new dialog that allows you to "scan" your account in order to add all users from your followers/following list. This process will read your data directly from Twitter and depending in the amount of people you have in your account it might take too many API calls. Please use it with caution. You can, for example, do the process separately for your followers/following people so it will be easier to handle, in case you have a massive amount of people. If TWBlue is unable to complete the scan, you will see an error and will be prompted to try again in 15 minutes, once your API calls have refreshed.
|
||||
* It is possible to use the user autocompletion functionality in dialogs where you can select an user, for example when adding or removing someone from a list, or displaying lists for someone.
|
||||
* Implemented a new setting, available in the account settings dialog, that allows to hide emojis in twitter usernames.
|
||||
* TWBlue should be able to sort conversations in a more logical way. This should make it easier to follow a long thread in Twitter.
|
||||
* When opening a thread, TWBlue should be able to load the right conversation if the original tweet from where the thread was loaded was a retweet.
|
||||
* TWBlue will restart the Streaming subsystem every time there are changes to followed, muted or blocked users within the application.
|
||||
* Fixed error when attempting to mention an user by using the "mention" button in any people buffer. Now tweets should be posted normally.
|
||||
* Fixed error when loading other user lists. ([#465](https://github.com/MCV-Software/TWBlue/issues/465))
|
||||
* Fixed an issue that was making TWBlue to display incorrectly some retweets of quoted tweets.
|
||||
* If TWBlue is unable to open a timeline for someone who has blocked you, this will be reported in a dialog. ([#485,](https://github.com/mcv-software/twblue/issues/485))
|
||||
* Added "find a string in the currently focused buffer" action into Windows 10 and windows 11 keymap. ([#476](https://github.com/MCV-Software/TWBlue/pull/476))
|
||||
|
||||
## changes in version 22.2.23
|
||||
|
||||
* We have added Experimental support for templates in the invisible interface. The GUI will remain unchanged for now:
|
||||
* Each object (tweet, received direct message, sent direct message and people) has its own template in the settings. You can edit those templates from the account settings dialog, in the new "templates" tab.
|
||||
* Every template is composed of the group of variables you want to display for each object. Each variable will start with a dollar sign ($) and cannot contain spaces or special characters. Templates can include arbitrary text that will not be processed. When editing the example templates, you can get an idea of the variables that are available for each object by using the template editing dialog. When you press enter on a variable from the list of available variables, it will be added to the template automatically. When you try to save a template, TWBlue will warn you if the template is incorrectly formatted or if it includes variables that do not exist in the information provided by objects. It is also possible to return to default values from the same dialog when editing a template.
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
wxpython
|
||||
wxpython==4.1.1
|
||||
wheel
|
||||
six
|
||||
configobj
|
||||
@@ -48,6 +48,7 @@ importlib-metadata
|
||||
numpy
|
||||
pillow
|
||||
charset-normalizer
|
||||
demoji
|
||||
git+https://github.com/accessibleapps/libloader
|
||||
git+https://github.com/accessibleapps/platform_utils
|
||||
git+https://github.com/accessibleapps/accessible_output2
|
||||
|
@@ -14,6 +14,7 @@ retweet_mode = string(default="ask")
|
||||
persist_size = integer(default=0)
|
||||
load_cache_in_memory=boolean(default=True)
|
||||
show_screen_names = boolean(default=False)
|
||||
hide_emojis = boolean(default=False)
|
||||
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted'))
|
||||
|
||||
[sound]
|
||||
|
@@ -9,6 +9,6 @@ copyright = "Copyright (C) 2013-2022, MCV Software."
|
||||
description = name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features."
|
||||
translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Corentin Bacqué-Cazenave (Français)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"]
|
||||
url = "https://twblue.es"
|
||||
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
|
||||
report_bugs_url = "https://github.com/mcvsoftware/twblue/issues"
|
||||
supported_languages = []
|
||||
version = "11"
|
||||
|
2
src/controller/buffers/mastodon/__init__.py
Normal file
2
src/controller/buffers/mastodon/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .base import BaseBuffer
|
141
src/controller/buffers/mastodon/base.py
Normal file
141
src/controller/buffers/mastodon/base.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Common logic to all buffers in TWBlue."""
|
||||
import logging
|
||||
import wx
|
||||
import output
|
||||
import sound
|
||||
import widgetUtils
|
||||
|
||||
log = logging.getLogger("controller.buffers.base.base")
|
||||
|
||||
class Buffer(object):
|
||||
""" A basic buffer object. This should be the base class for all other derived buffers."""
|
||||
|
||||
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
|
||||
"""Inits the main controller for this buffer:
|
||||
@ parent wx.Treebook object: Container where we will put this buffer.
|
||||
@ function str or None: function to be called periodically and update items on this buffer.
|
||||
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
|
||||
"""
|
||||
super(Buffer, self).__init__()
|
||||
self.function = function
|
||||
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
|
||||
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
|
||||
# Read more about compose functions in sessions/twitter/compose.py.
|
||||
self.compose_function = None
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
|
||||
self.buffer = None
|
||||
# This should countains the account associated to this buffer.
|
||||
self.account = ""
|
||||
# This controls whether the start_stream function should be called when starting the program.
|
||||
self.needs_init = True
|
||||
# if this is set to False, the buffer will be ignored on the invisible interface.
|
||||
self.invisible = False
|
||||
# Control variable, used to track time of execution for calls to start_stream.
|
||||
self.execution_time = 0
|
||||
|
||||
def clear_list(self):
|
||||
pass
|
||||
|
||||
def get_event(self, ev):
|
||||
""" Catch key presses in the WX interface and generate the corresponding event names."""
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
|
||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
|
||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
||||
# Raise a Special event when pressed Shift+F10 because Wx==4.1.x does not seems to trigger this by itself.
|
||||
# See https://github.com/manuelcortez/TWBlue/issues/353
|
||||
elif ev.GetKeyCode() == wx.WXK_F10 and ev.ShiftDown(): event = "show_menu"
|
||||
else:
|
||||
event = None
|
||||
ev.Skip()
|
||||
if event != None:
|
||||
try:
|
||||
### ToDo: Remove after WX fixes issue #353 in the widgets.
|
||||
if event == "show_menu":
|
||||
return self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
|
||||
getattr(self, event)()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def volume_down(self):
|
||||
""" Decreases volume by 5%"""
|
||||
if self.session.settings["sound"]["volume"] > 0.0:
|
||||
if self.session.settings["sound"]["volume"] <= 0.05:
|
||||
self.session.settings["sound"]["volume"] = 0.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] -=0.05
|
||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
self.session.settings.write()
|
||||
|
||||
def volume_up(self):
|
||||
""" Increases volume by 5%."""
|
||||
if self.session.settings["sound"]["volume"] < 1.0:
|
||||
if self.session.settings["sound"]["volume"] >= 0.95:
|
||||
self.session.settings["sound"]["volume"] = 1.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] +=0.05
|
||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
self.session.settings.write()
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True):
|
||||
pass
|
||||
|
||||
def get_more_items(self):
|
||||
output.speak(_(u"This action is not supported for this buffer"), True)
|
||||
|
||||
def put_items_on_list(self, items):
|
||||
pass
|
||||
|
||||
def remove_buffer(self):
|
||||
return False
|
||||
|
||||
def remove_item(self, item):
|
||||
f = self.buffer.list.get_selected()
|
||||
self.buffer.list.remove_item(item)
|
||||
self.buffer.list.select_item(f)
|
||||
|
||||
def bind_events(self):
|
||||
pass
|
||||
|
||||
def get_object(self):
|
||||
return self.buffer
|
||||
|
||||
def get_message(self):
|
||||
pass
|
||||
|
||||
def set_list_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.buffer.list.select_item(-1)
|
||||
else:
|
||||
self.buffer.list.select_item(0)
|
||||
|
||||
def reply(self):
|
||||
pass
|
||||
|
||||
def send_message(self):
|
||||
pass
|
||||
|
||||
def share_item(self):
|
||||
pass
|
||||
|
||||
def can_share(self):
|
||||
pass
|
||||
|
||||
def destroy_status(self):
|
||||
pass
|
||||
|
||||
def post_status(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def save_positions(self):
|
||||
try:
|
||||
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
||||
except AttributeError:
|
||||
pass
|
@@ -98,7 +98,7 @@ class PeopleBuffer(base.BaseBuffer):
|
||||
message = messages.tweet(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False)
|
||||
if message.message.ShowModal() == widgetUtils.OK:
|
||||
tweet_data = message.get_tweet_data()
|
||||
call_threaded(self.session.send_tweet, tweet_data)
|
||||
call_threaded(self.session.send_tweet, *tweet_data)
|
||||
if hasattr(message.message, "destroy"):
|
||||
message.message.destroy()
|
||||
|
||||
|
@@ -153,6 +153,7 @@ class ConversationBuffer(SearchBuffer):
|
||||
results.extend(reply_results)
|
||||
except TweepyException as e:
|
||||
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
||||
results.sort(key=lambda x: x.id)
|
||||
return results
|
||||
|
||||
def get_replies_v1(self, tweet):
|
||||
|
@@ -32,7 +32,7 @@ class listsController(object):
|
||||
return [compose.compose_list(item) for item in self.session.db["lists"]]
|
||||
|
||||
def get_user_lists(self, user):
|
||||
self.lists = self.session.twitter.lists_all(reverse=True, screen_name=user)
|
||||
self.lists = self.session.twitter.get_lists(reverse=True, screen_name=user)
|
||||
return [compose.compose_list(item) for item in self.lists]
|
||||
|
||||
def create_list(self, *args, **kwargs):
|
||||
|
@@ -18,7 +18,7 @@ if system == "Windows":
|
||||
from . import user
|
||||
from . import listsController
|
||||
from . import filterController
|
||||
# from issueReporter import issueReporter
|
||||
from . import userSelector
|
||||
elif system == "Linux":
|
||||
from gtkUI import (view, commonMessageDialogs)
|
||||
from sessions.twitter import utils, compose
|
||||
@@ -135,6 +135,7 @@ class Controller(object):
|
||||
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
|
||||
pub.subscribe(self.create_buffer, "createBuffer")
|
||||
pub.subscribe(self.toggle_share_settings, "toggleShare")
|
||||
pub.subscribe(self.restart_streaming, "restartStreaming")
|
||||
if system == "Windows":
|
||||
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
|
||||
@@ -185,7 +186,6 @@ class Controller(object):
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.seekRight, menuitem=self.view.seekRight)
|
||||
if widgetUtils.toolkit == "wx":
|
||||
widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_alias, self.view.addAlias)
|
||||
@@ -520,10 +520,9 @@ class Controller(object):
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user = dlg.get_user()
|
||||
else:
|
||||
selector = userSelector.userSelector(users=users, session_id=buff.session.session_id)
|
||||
user = selector.get_user()
|
||||
if user == None:
|
||||
return
|
||||
l = listsController.listsController(buff.session, user=user)
|
||||
|
||||
@@ -537,10 +536,9 @@ class Controller(object):
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user = dlg.get_user()
|
||||
else:
|
||||
selector = userSelector.userSelector(users=users, session_id=buff.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 buff.session.db["lists"]])
|
||||
@@ -566,10 +564,9 @@ class Controller(object):
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user = dlg.get_user()
|
||||
else:
|
||||
selector = userSelector.userSelector(users=users, session_id=buff.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 buff.session.db["lists"]])
|
||||
@@ -611,9 +608,6 @@ class Controller(object):
|
||||
buff.session.save_persistent_data()
|
||||
restart.restart_program()
|
||||
|
||||
def report_error(self, *args, **kwargs):
|
||||
r = issueReporter.reportBug(self.get_best_buffer().session.db["user_name"])
|
||||
|
||||
def check_for_updates(self, *args, **kwargs):
|
||||
update = updater.do_update()
|
||||
if update == False:
|
||||
@@ -876,7 +870,7 @@ class Controller(object):
|
||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended")
|
||||
try:
|
||||
tl.start_stream(play_sound=False)
|
||||
except ValueError:
|
||||
except TweepyException:
|
||||
commonMessageDialogs.unauthorized()
|
||||
return
|
||||
pos=self.view.search("timelines", buff.session.db["user_name"])
|
||||
@@ -948,10 +942,12 @@ class Controller(object):
|
||||
|
||||
def open_conversation(self, *args, **kwargs):
|
||||
buffer = self.get_current_buffer()
|
||||
id = buffer.get_right_tweet().id
|
||||
user = buffer.session.get_user(buffer.get_right_tweet().user).screen_name
|
||||
search = buffers.twitter.ConversationBuffer(self.view.nb, "search_tweets", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
|
||||
search.tweet = buffer.get_right_tweet()
|
||||
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
|
||||
search = buffers.twitter.ConversationBuffer(self.view.nb, "search_tweets", "%s-searchterm" % (tweet.id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=tweet.id, q="@{0}".format(user,))
|
||||
search.tweet = tweet
|
||||
search.start_stream(start=True)
|
||||
pos=self.view.search("searches", buffer.session.db["user_name"])
|
||||
self.insert_buffer(search, pos)
|
||||
@@ -1661,7 +1657,6 @@ class Controller(object):
|
||||
def toggle_share_settings(self, shareable=True):
|
||||
self.view.retweet.Enable(shareable)
|
||||
|
||||
|
||||
def check_streams(self):
|
||||
if self.started == False:
|
||||
return
|
||||
@@ -1671,3 +1666,10 @@ class Controller(object):
|
||||
sessions.sessions[i].check_streams()
|
||||
except TweepyException: # We shouldn't allow this function to die.
|
||||
pass
|
||||
|
||||
def restart_streaming(self, session):
|
||||
for s in sessions.sessions:
|
||||
if sessions.sessions[s].session_id == session:
|
||||
log.debug("Restarting stream in session %s" % (session))
|
||||
sessions.sessions[s].stop_streaming()
|
||||
sessions.sessions[s].start_streaming()
|
@@ -11,8 +11,9 @@ from pubsub import pub
|
||||
from twitter_text import parse_tweet
|
||||
from wxUI.dialogs import twitterDialogs, urlList
|
||||
from wxUI import commonMessageDialogs
|
||||
from extra import translator, SpellChecker, autocompletionUsers
|
||||
from extra import translator, SpellChecker
|
||||
from extra.AudioUploader import audioUploader
|
||||
from extra.autocompletionUsers import completion
|
||||
from sessions.twitter import utils
|
||||
|
||||
class basicTweet(object):
|
||||
@@ -160,7 +161,7 @@ class tweet(basicTweet):
|
||||
self.text_processor()
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c = completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu()
|
||||
|
||||
def add_tweet(self, event, update_gui=True, *args, **kwargs):
|
||||
@@ -269,7 +270,7 @@ class dm(basicTweet):
|
||||
self.text_processor()
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c = completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu("dm")
|
||||
|
||||
def text_processor(self, *args, **kwargs):
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import webbrowser
|
||||
import threading
|
||||
import logging
|
||||
import sound_lib
|
||||
import paths
|
||||
@@ -16,7 +17,7 @@ from pubsub import pub
|
||||
from mysc import autostart as autostart_windows
|
||||
from wxUI.dialogs import configuration
|
||||
from wxUI import commonMessageDialogs
|
||||
from extra.autocompletionUsers import settings
|
||||
from extra.autocompletionUsers import scan, manage
|
||||
from extra.ocr import OCRSpace
|
||||
from .editTemplateController import EditTemplate
|
||||
|
||||
@@ -142,9 +143,11 @@ class accountSettingsController(globalSettingsController):
|
||||
|
||||
def create_config(self):
|
||||
self.dialog.create_general_account()
|
||||
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
|
||||
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"]
|
||||
@@ -228,7 +231,7 @@ class accountSettingsController(globalSettingsController):
|
||||
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.settings["templates"]["person"]
|
||||
template = self.config["templates"]["person"]
|
||||
control = EditTemplate(template=template, type="person")
|
||||
result = control.run_dialog()
|
||||
if result != "": # Template has been saved.
|
||||
@@ -242,6 +245,7 @@ class accountSettingsController(globalSettingsController):
|
||||
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")
|
||||
@@ -302,8 +306,19 @@ class accountSettingsController(globalSettingsController):
|
||||
def toggle_state(self,*args,**kwargs):
|
||||
return self.dialog.buffers.change_selected_item()
|
||||
|
||||
def manage_autocomplete(self, *args, **kwargs):
|
||||
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
|
||||
def 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()
|
||||
|
@@ -107,7 +107,7 @@ class profileController(object):
|
||||
string = string+ _(u"Protected: %s\n") % (protected)
|
||||
if hasattr(self, "friendship_status"):
|
||||
relation = False
|
||||
friendship = "Relationship: "
|
||||
friendship = _(u"Relationship: ")
|
||||
if self.friendship_status[0].following:
|
||||
friendship += _(u"You follow {0}. ").format(self.data.name,)
|
||||
relation = True
|
||||
|
@@ -4,7 +4,7 @@ import output
|
||||
from wxUI.dialogs import userActions
|
||||
from pubsub import pub
|
||||
from tweepy.errors import TweepyException
|
||||
from extra import autocompletionUsers
|
||||
from extra.autocompletionUsers import completion
|
||||
|
||||
class userActionsController(object):
|
||||
def __init__(self, buffer, users=[], default="follow"):
|
||||
@@ -17,7 +17,7 @@ class userActionsController(object):
|
||||
self.process_action()
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
|
||||
c = completion.autocompletionUsers(self.dialog, self.session.session_id)
|
||||
c.show_menu("dm")
|
||||
|
||||
def process_action(self):
|
||||
@@ -29,36 +29,42 @@ class userActionsController(object):
|
||||
def follow(self, user):
|
||||
try:
|
||||
self.session.twitter.create_friendship(screen_name=user )
|
||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def unfollow(self, user):
|
||||
try:
|
||||
id = self.session.twitter.destroy_friendship(screen_name=user )
|
||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def mute(self, user):
|
||||
try:
|
||||
id = self.session.twitter.create_mute(screen_name=user )
|
||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def unmute(self, user):
|
||||
try:
|
||||
id = self.session.twitter.destroy_mute(screen_name=user )
|
||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def report(self, user):
|
||||
try:
|
||||
id = self.session.twitter.report_spam(screen_name=user )
|
||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def block(self, user):
|
||||
try:
|
||||
id = self.session.twitter.create_block(screen_name=user )
|
||||
pub.sendMessage("restartStreaming", session=self.session.session_id)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
|
@@ -2,7 +2,6 @@
|
||||
import widgetUtils
|
||||
from pubsub import pub
|
||||
from wxUI.dialogs import userAliasDialogs
|
||||
from extra import autocompletionUsers
|
||||
|
||||
class userAliasController(object):
|
||||
def __init__(self, settings):
|
||||
|
39
src/controller/userSelector.py
Normal file
39
src/controller/userSelector.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Small utility dessigned to select users from the currently focused item or the autocomplete database. """
|
||||
import wx
|
||||
import widgetUtils
|
||||
from wxUI.dialogs import utils
|
||||
from extra.autocompletionUsers import completion
|
||||
|
||||
class userSelector(object):
|
||||
|
||||
def __init__(self, users, session_id, title=_("Select user")):
|
||||
""" Creates a dialog that chooses an user selector, from where users who have the autocomplete database already filled can also use that feature.
|
||||
|
||||
:param users: lists of users extracted from the currently focused item.
|
||||
:type users: list
|
||||
:param session_id: ID of the session to instantiate autocomplete database.
|
||||
:type session_id: str
|
||||
:param title: Title of the user selector dialog.
|
||||
:type title: str
|
||||
"""
|
||||
self.session_id = session_id
|
||||
self.dlg = utils.selectUserDialog(users=users, title=title)
|
||||
widgetUtils.connect_event(self.dlg.autocompletion, widgetUtils.BUTTON_PRESSED, self.on_autocomplete_users)
|
||||
|
||||
def on_autocomplete_users(self, *args, **kwargs):
|
||||
""" performs user autocompletion, if configured properly. """
|
||||
c = completion.autocompletionUsers(self.dlg, self.session_id)
|
||||
c.show_menu("dm")
|
||||
|
||||
def get_user(self):
|
||||
""" Actually shows the dialog and returns an user if the dialog was accepted, None otherwise.
|
||||
|
||||
:rtype: str or None
|
||||
"""
|
||||
if self.dlg.ShowModal() == wx.ID_OK:
|
||||
user = self.dlg.get_user()
|
||||
else:
|
||||
user = None
|
||||
self.dlg.Destroy()
|
||||
return user
|
@@ -1,5 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import completion, settings
|
||||
""" Autocompletion users for TWBlue. This package contains all needed code to support this feature, including automatic addition of users, management and code to show the autocompletion menu when an user is composing a tweet. """
|
||||
|
@@ -1,17 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Module to display the user autocompletion menu in tweet or direct message dialogs. """
|
||||
import output
|
||||
from . import storage
|
||||
from . import wx_menu
|
||||
|
||||
class autocompletionUsers(object):
|
||||
def __init__(self, window, session_id):
|
||||
""" Class constructor. Displays a menu with users matching the specified pattern for autocompletion.
|
||||
|
||||
:param window: A wx control where the menu should be displayed. Normally this is going to be the wx.TextCtrl indicating the tweet's text or direct message recipient.
|
||||
:type window: wx.Dialog
|
||||
:param session_id: Session ID which calls this class. We will load the users database from this session.
|
||||
:type session_id: str.
|
||||
"""
|
||||
super(autocompletionUsers, self).__init__()
|
||||
self.window = window
|
||||
self.db = storage.storage(session_id)
|
||||
|
||||
def show_menu(self, mode="tweet"):
|
||||
position = self.window.text.GetInsertionPoint()
|
||||
""" displays a menu with possible users matching the specified pattern.
|
||||
|
||||
this menu can be displayed in tweet dialogs or in any other dialog where an username is expected. For tweet dialogs, the string should start with an at symbol (@), otherwise it won't match the pattern.
|
||||
|
||||
Of course, users must be already loaded in database before attempting this.
|
||||
|
||||
If no users are found, an error message will be spoken.
|
||||
|
||||
:param mode: this controls how the dialog will behave. Possible values are 'tweet' and 'dm'. In tweet mode, the matching pattern will be @user (@ is required), while in 'dm' mode the matching pattern will be anything written in the text control.
|
||||
:type mode: str
|
||||
"""
|
||||
if mode == "tweet":
|
||||
position = self.window.text.GetInsertionPoint()
|
||||
text = self.window.text.GetValue()
|
||||
text = text[:position]
|
||||
try:
|
||||
@@ -41,7 +60,7 @@ class autocompletionUsers(object):
|
||||
users = self.db.get_users(pattern)
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.PopupMenu(menu, self.window.text.GetPosition())
|
||||
self.window.PopupMenu(menu, self.window.cb.GetPosition())
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
||||
|
@@ -1,15 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Management of users in the local database for autocompletion. """
|
||||
import time
|
||||
import widgetUtils
|
||||
from tweepy.cursor import Cursor
|
||||
from tweepy.errors import TweepyException
|
||||
from . import storage, wx_manage
|
||||
from wxUI import commonMessageDialogs
|
||||
from . import storage, wx_manage
|
||||
|
||||
class autocompletionManage(object):
|
||||
def __init__(self, session):
|
||||
""" class constructor. Manages everything related to user autocompletion.
|
||||
|
||||
:param session: Sessiom where the autocompletion management has been requested.
|
||||
:type session: sessions.base.Session.
|
||||
"""
|
||||
super(autocompletionManage, self).__init__()
|
||||
self.session = session
|
||||
self.dialog = wx_manage.autocompletionManageDialog()
|
||||
# Instantiate database so we can perform modifications on it.
|
||||
self.database = storage.storage(self.session.session_id)
|
||||
|
||||
def show_settings(self):
|
||||
""" display user management dialog and connect events associated to it. """
|
||||
self.dialog = wx_manage.autocompletionManageDialog()
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
||||
@@ -17,6 +29,7 @@ class autocompletionManage(object):
|
||||
self.dialog.get_response()
|
||||
|
||||
def update_list(self):
|
||||
""" update users list in management dialog. This function is normallhy used after we modify the database in any way, so we can reload all users in the autocompletion user management list. """
|
||||
item = self.dialog.users.get_selected()
|
||||
self.dialog.users.clear()
|
||||
self.users = self.database.get_all_users()
|
||||
@@ -24,9 +37,12 @@ class autocompletionManage(object):
|
||||
self.dialog.users.select_item(item)
|
||||
|
||||
def add_user(self, *args, **kwargs):
|
||||
""" Add a new Twitter username to the autocompletion database. """
|
||||
usr = self.dialog.get_user()
|
||||
if usr == False:
|
||||
return
|
||||
# check if user exists.
|
||||
# ToDo: in case we want to adapt this for other networks we'd need to refactor this check.
|
||||
try:
|
||||
data = self.session.twitter.get_user(screen_name=usr)
|
||||
except TweepyException as e:
|
||||
@@ -36,7 +52,8 @@ class autocompletionManage(object):
|
||||
self.database.set_user(data.screen_name, data.name, 0)
|
||||
self.update_list()
|
||||
|
||||
def remove_user(self, ev):
|
||||
def remove_user(self, *args, **kwargs):
|
||||
""" Remove focused user from the autocompletion database. """
|
||||
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
||||
item = self.dialog.users.get_selected()
|
||||
user = self.users[item]
|
||||
|
110
src/extra/autocompletionUsers/scan.py
Normal file
110
src/extra/autocompletionUsers/scan.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Scanning code for autocompletion feature on TWBlue. This module can retrieve user objects from the selected Twitter account automatically. """
|
||||
import time
|
||||
import wx
|
||||
import widgetUtils
|
||||
import output
|
||||
from tweepy.cursor import Cursor
|
||||
from tweepy.errors import TweepyException
|
||||
from pubsub import pub
|
||||
from . import wx_scan
|
||||
from . import manage
|
||||
from . import storage
|
||||
|
||||
class autocompletionScan(object):
|
||||
def __init__(self, config, buffer, window):
|
||||
""" Class constructor. This class will take care of scanning the selected Twitter account to populate the database with users automatically upon request.
|
||||
|
||||
:param config: Config for the session that will be scanned in search for users.
|
||||
:type config: dict
|
||||
:param buffer: home buffer for the focused session.
|
||||
:type buffer: controller.buffers.twitter.base.baseBuffer
|
||||
:param window: Main Window of TWBlue.
|
||||
:type window:wx.Frame
|
||||
"""
|
||||
super(autocompletionScan, self).__init__()
|
||||
self.config = config
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
|
||||
def show_dialog(self):
|
||||
self.dialog = wx_scan.autocompletionScanDialog()
|
||||
self.dialog.set("friends", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
||||
self.dialog.set("followers", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
confirmation = wx_scan.confirm()
|
||||
return confirmation
|
||||
|
||||
def prepare_progress_dialog(self):
|
||||
self.progress_dialog = wx_scan.autocompletionScanProgressDialog()
|
||||
# connect method to update progress dialog
|
||||
pub.subscribe(self.on_update_progress, "on-update-progress")
|
||||
self.progress_dialog.Show()
|
||||
|
||||
def on_update_progress(self, percent):
|
||||
if percent > 100:
|
||||
percent = 100
|
||||
wx.CallAfter(self.progress_dialog.update, percent)
|
||||
|
||||
def scan(self):
|
||||
""" Attempts to add all users selected by current user to the autocomplete database. """
|
||||
ids = []
|
||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends")
|
||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers")
|
||||
output.speak(_("Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||
database = storage.storage(self.buffer.session.session_id)
|
||||
percent = 0
|
||||
# Retrieve ids of all following users
|
||||
if self.dialog.get("friends") == True:
|
||||
for i in Cursor(self.buffer.session.twitter.get_friend_ids, count=5000).items():
|
||||
if str(i) not in ids:
|
||||
ids.append(str(i))
|
||||
# same step, but for followers.
|
||||
if self.dialog.get("followers") == True:
|
||||
try:
|
||||
for i in Cursor(self.buffer.session.twitter.get_follower_ids, count=5000).items():
|
||||
if str(i) not in ids:
|
||||
ids.append(str(i))
|
||||
except TweepyException:
|
||||
wx.CallAfter(wx_scan.show_error)
|
||||
return self.done()
|
||||
# As next step requires batches of 100s users, let's split our user Ids so we won't break the param rules.
|
||||
split_users = [ids[i:i + 100] for i in range(0, len(ids), 100)]
|
||||
# store returned users in this list.
|
||||
users = []
|
||||
for z in split_users:
|
||||
if len(z) == 0:
|
||||
continue
|
||||
try:
|
||||
results = self.buffer.session.twitter.lookup_users(user_id=z)
|
||||
except TweepyException:
|
||||
wx.CallAfter(wx_scan.show_error)
|
||||
return self.done()
|
||||
users.extend(results)
|
||||
time.sleep(1)
|
||||
percent = percent + (100/len(split_users))
|
||||
pub.sendMessage("on-update-progress", percent=percent)
|
||||
for user in users:
|
||||
database.set_user(user.screen_name, user.name, 1)
|
||||
wx.CallAfter(wx_scan.show_success, len(users))
|
||||
self.done()
|
||||
|
||||
def done(self):
|
||||
wx.CallAfter(self.progress_dialog.Destroy)
|
||||
wx.CallAfter(self.dialog.Destroy)
|
||||
pub.unsubscribe(self.on_update_progress, "on-update-progress")
|
||||
|
||||
def execute_at_startup(window, buffer, config):
|
||||
database = storage.storage(buffer.session.session_id)
|
||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
||||
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
||||
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
@@ -1,59 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import widgetUtils
|
||||
import output
|
||||
from . import wx_settings
|
||||
from . import manage
|
||||
from . import storage
|
||||
from mysc.thread_utils import call_threaded
|
||||
|
||||
class autocompletionSettings(object):
|
||||
def __init__(self, config, buffer, window):
|
||||
super(autocompletionSettings, self).__init__()
|
||||
self.config = config
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.dialog = wx_settings.autocompletionSettingsDialog()
|
||||
self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
||||
self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
||||
widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
call_threaded(self.add_users_to_database)
|
||||
|
||||
def add_users_to_database(self):
|
||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer")
|
||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer")
|
||||
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||
database = storage.storage(self.buffer.session.session_id)
|
||||
if self.dialog.get("followers_buffer") == True:
|
||||
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if self.dialog.get("friends_buffer") == True:
|
||||
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
wx_settings.show_success_dialog()
|
||||
self.dialog.destroy()
|
||||
|
||||
def view_list(self, ev):
|
||||
q = manage.autocompletionManage(self.buffer.session)
|
||||
|
||||
|
||||
def execute_at_startup(window, buffer, config):
|
||||
database = storage.storage(buffer.session.session_id)
|
||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
||||
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
||||
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
@@ -11,7 +11,7 @@ class menu(wx.Menu):
|
||||
def append_options(self, options):
|
||||
for i in options:
|
||||
item = wx.MenuItem(self, wx.ID_ANY, "%s (@%s)" % (i[1], i[0]))
|
||||
self.AppendItem(item)
|
||||
self.Append(item)
|
||||
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
||||
|
||||
def select_text(self, ev, text):
|
||||
|
48
src/extra/autocompletionUsers/wx_scan.py
Normal file
48
src/extra/autocompletionUsers/wx_scan.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
|
||||
class autocompletionScanDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(autocompletionScanDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.followers = wx.CheckBox(panel, -1, _("Add followers to database"))
|
||||
self.friends = wx.CheckBox(panel, -1, _("Add friends to database"))
|
||||
sizer.Add(self.followers, 0, wx.ALL, 5)
|
||||
sizer.Add(self.friends, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
class autocompletionScanProgressDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(autocompletionScanProgressDialog, self).__init__(parent=None, id=wx.ID_ANY, title=_("Updating autocompletion database"), *args, **kwargs)
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.progress_bar = wx.Gauge(parent=panel)
|
||||
sizer.Add(self.progress_bar)
|
||||
panel.SetSizerAndFit(sizer)
|
||||
|
||||
def update(self, percent):
|
||||
self.progress_bar.SetValue(percent)
|
||||
|
||||
def confirm():
|
||||
with wx.MessageDialog(None, _("This process will retrieve the users you selected from Twitter, and add them to the user autocomplete database. Please note that if there are many users or you have tried to perform this action less than 15 minutes ago, TWBlue may reach a limit in Twitter API calls when trying to load the users into the database. If this happens, we will show you an error, in which case you will have to try this process again in a few minutes. If this process ends with no error, you will be redirected back to the account settings dialog. Do you want to continue?"), _("Attention"), style=wx.ICON_QUESTION|wx.YES_NO) as result:
|
||||
if result.ShowModal() == wx.ID_YES:
|
||||
return True
|
||||
return False
|
||||
|
||||
def show_success(users):
|
||||
with wx.MessageDialog(None, _("TWBlue has imported {} users successfully.").format(users), _("Done")) as dlg:
|
||||
dlg.ShowModal()
|
||||
|
||||
def show_error():
|
||||
with wx.MessageDialog(None, _("Error adding users from Twitter. Please try again in about 15 minutes."), _("Error"), style=wx.ICON_ERROR) as dlg:
|
||||
dlg.ShowModal()
|
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
|
||||
class autocompletionSettingsDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer"))
|
||||
self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer"))
|
||||
sizer.Add(self.followers_buffer, 0, wx.ALL, 5)
|
||||
sizer.Add(self.friends_buffer, 0, wx.ALL, 5)
|
||||
self.viewList = wx.Button(panel, -1, _(u"Manage database..."))
|
||||
sizer.Add(self.viewList, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def show_success_dialog():
|
||||
wx.MessageDialog(None, _(u"{0}'s database of users has been updated.").format(application.name,), _(u"Done"), wx.OK).ShowModal()
|
@@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
from __future__ import unicode_literals
|
||||
categories = ["General"]
|
||||
reproducibilities = ["always", "sometimes", "random", "have not tried", "unable to duplicate"]
|
||||
severities = ["block", "crash", "major", "minor", "tweak", "text", "trivial", "feature"]
|
@@ -1,66 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import keys
|
||||
import wx
|
||||
import wx_ui
|
||||
import widgetUtils
|
||||
import application
|
||||
from suds.client import Client
|
||||
import constants
|
||||
|
||||
class reportBug(object):
|
||||
def __init__(self, user_name):
|
||||
self.user_name = user_name
|
||||
self.categories = [_(u"General")]
|
||||
self.reproducibilities = [_(u"always"), _(u"sometimes"), _(u"random"), _(u"have not tried"), _(u"unable to duplicate")]
|
||||
self.severities = [_(u"block"), _(u"crash"), _(u"major"), _(u"minor"), _(u"tweak"), _(u"text"), _(u"trivial"), _(u"feature")]
|
||||
self.dialog = wx_ui.reportBugDialog(self.categories, self.reproducibilities, self.severities)
|
||||
widgetUtils.connect_event(self.dialog.ok, widgetUtils.BUTTON_PRESSED, self.send)
|
||||
self.dialog.get_response()
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
if self.dialog.get("summary") == "" or self.dialog.get("description") == "":
|
||||
self.dialog.no_filled()
|
||||
return
|
||||
if self.dialog.get("agree") == False:
|
||||
self.dialog.no_checkbox()
|
||||
return
|
||||
try:
|
||||
client = Client(application.report_bugs_url)
|
||||
issue = client.factory.create('IssueData')
|
||||
issue.project.name = application.name
|
||||
issue.project.id = 0
|
||||
issue.summary = self.dialog.get("summary"),
|
||||
issue.description = "Reported by @%s on version %s (snapshot = %s)\n\n" % (self.user_name, application.version, application.snapshot) + self.dialog.get("description")
|
||||
# to do: Create getters for category, severity and reproducibility in wx_UI.
|
||||
issue.category = constants.categories[self.dialog.category.GetSelection()]
|
||||
issue.reproducibility.name = constants.reproducibilities[self.dialog.reproducibility.GetSelection()]
|
||||
issue.severity.name = constants.severities[self.dialog.severity.GetSelection()]
|
||||
issue.priority.name = "normal"
|
||||
issue.view_state.name = "public"
|
||||
issue.resolution.name = "open"
|
||||
issue.projection.name = "none"
|
||||
issue.eta.name = "eta"
|
||||
issue.status.name = "new"
|
||||
id = client.service.mc_issue_add(keys.keyring.get("bts_user"), keys.keyring.get("bts_password"), issue)
|
||||
self.dialog.success(id)
|
||||
except:
|
||||
self.dialog.error()
|
@@ -1,95 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
from __future__ import unicode_literals
|
||||
import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
class reportBugDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, categories, reproducibilities, severities):
|
||||
super(reportBugDialog, self).__init__(parent=None, id=wx.NewId())
|
||||
self.SetTitle(_(u"Report an error"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
categoryLabel = wx.StaticText(panel, -1, _(u"Select a category"), size=wx.DefaultSize)
|
||||
self.category = wx.ComboBox(panel, -1, choices=categories, style=wx.CB_READONLY)
|
||||
self.category.SetSelection(0)
|
||||
categoryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
categoryB.Add(categoryLabel, 0, wx.ALL, 5)
|
||||
categoryB.Add(self.category, 0, wx.ALL, 5)
|
||||
self.category.SetFocus()
|
||||
sizer.Add(categoryB, 0, wx.ALL, 5)
|
||||
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
|
||||
self.summary = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.summary)
|
||||
dc.SetFont(self.summary.GetFont())
|
||||
self.summary.SetSize(dc.GetTextExtent("a"*80))
|
||||
summaryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
|
||||
summaryB.Add(self.summary, 0, wx.ALL, 5)
|
||||
sizer.Add(summaryB, 0, wx.ALL, 5)
|
||||
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
|
||||
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
|
||||
dc = wx.WindowDC(self.description)
|
||||
dc.SetFont(self.description.GetFont())
|
||||
(x, y, z) = dc.GetMultiLineTextExtent("0"*2000)
|
||||
self.description.SetSize((x, y))
|
||||
descBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
|
||||
descBox.Add(self.description, 0, wx.ALL, 5)
|
||||
sizer.Add(descBox, 0, wx.ALL, 5)
|
||||
reproducibilityLabel = wx.StaticText(panel, -1, _(u"how often does this bug happen?"), size=wx.DefaultSize)
|
||||
self.reproducibility = wx.ComboBox(panel, -1, choices=reproducibilities, style=wx.CB_READONLY)
|
||||
self.reproducibility.SetSelection(3)
|
||||
reprB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
reprB.Add(reproducibilityLabel, 0, wx.ALL, 5)
|
||||
reprB.Add(self.reproducibility, 0, wx.ALL, 5)
|
||||
sizer.Add(reprB, 0, wx.ALL, 5)
|
||||
severityLabel = wx.StaticText(panel, -1, _(u"Select the importance that you think this bug has"))
|
||||
self.severity = wx.ComboBox(panel, -1, choices=severities, style=wx.CB_READONLY)
|
||||
self.severity.SetSelection(3)
|
||||
severityB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
severityB.Add(severityLabel, 0, wx.ALL, 5)
|
||||
severityB.Add(self.severity, 0, wx.ALL, 5)
|
||||
sizer.Add(severityB, 0, wx.ALL, 5)
|
||||
self.agree = wx.CheckBox(panel, -1, _(u"I know that the {0} bug system will get my Twitter username to contact me and fix the bug quickly").format(application.name,))
|
||||
self.agree.SetValue(False)
|
||||
sizer.Add(self.agree, 0, wx.ALL, 5)
|
||||
self.ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
|
||||
self.ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ok, 0, wx.ALL, 5)
|
||||
btnBox.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def no_filled(self):
|
||||
wx.MessageDialog(self, _(u"You must fill out both fields"), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def no_checkbox(self):
|
||||
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your twitter username to contact you if it is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def success(self, id):
|
||||
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
def error(self):
|
||||
wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_CANCEL)
|
@@ -55,4 +55,5 @@ accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+alt+shift+u")
|
||||
ocr_image = string(default="win+alt+o")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
||||
add_alias=string(default="")
|
||||
find = string(default="control+win+{")
|
||||
|
@@ -55,4 +55,5 @@ accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+alt+shift+u")
|
||||
ocr_image = string(default="win+alt+o")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
||||
add_alias=string(default="")
|
||||
find = string(default="control+win+{")
|
||||
|
File diff suppressed because it is too large
Load Diff
BIN
src/locales/ar/LC_MESSAGES/twblue.mo
Normal file
BIN
src/locales/ar/LC_MESSAGES/twblue.mo
Normal file
Binary file not shown.
3616
src/locales/ar/LC_MESSAGES/twblue.po
Normal file
3616
src/locales/ar/LC_MESSAGES/twblue.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
src/locales/he_il/LC_MESSAGES/twblue.mo
Normal file
BIN
src/locales/he_il/LC_MESSAGES/twblue.mo
Normal file
Binary file not shown.
3500
src/locales/he_il/LC_MESSAGES/twblue.po
Normal file
3500
src/locales/he_il/LC_MESSAGES/twblue.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3595
src/locales/it/LC_MESSAGES/twblue.po
Normal file
3595
src/locales/it/LC_MESSAGES/twblue.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/locales/ja/LC_MESSAGES/twblue.mo
Normal file
BIN
src/locales/ja/LC_MESSAGES/twblue.mo
Normal file
Binary file not shown.
3567
src/locales/ja/LC_MESSAGES/twblue.po
Normal file
3567
src/locales/ja/LC_MESSAGES/twblue.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
src/locales/mn/LC_MESSAGES/twblue.mo
Normal file
BIN
src/locales/mn/LC_MESSAGES/twblue.mo
Normal file
Binary file not shown.
3557
src/locales/mn/LC_MESSAGES/twblue.po
Normal file
3557
src/locales/mn/LC_MESSAGES/twblue.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" A cross platform notification system.
|
||||
Under Linux, the wx.NotificationMessage does not show a notification on the taskbar, so we decided to use dbus for showing notifications for linux and wx for Windows."""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import platform
|
||||
|
||||
notify = None
|
||||
|
||||
def setup():
|
||||
global notify
|
||||
if platform.system() == "Windows":
|
||||
from . import windows
|
||||
notify = windows.notification()
|
||||
elif platform.system() == "Linux":
|
||||
from . import linux
|
||||
notify = linux.notification()
|
||||
|
||||
def send(title, text):
|
||||
global notify
|
||||
if not notify or notify is None:
|
||||
setup()
|
||||
notify.notify(title, text)
|
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import dbus
|
||||
import application
|
||||
|
||||
class notifications(object):
|
||||
"""Supports notifications on Linux.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(notifications, self).__init__()
|
||||
self.item = "org.freedesktop.Notifications"
|
||||
self.path = "/org/freedesktop/Notifications"
|
||||
self.interface = "org.freedesktop.Notifications"
|
||||
self.app_name = application.name
|
||||
self.id_num_to_replace = 0
|
||||
self.icon = "/usr/share/icons/Tango/32x32/status/sunny.png"
|
||||
|
||||
def notify(self, title="", text=""):
|
||||
actions_list = ''
|
||||
hint = ''
|
||||
time = 5000 # Use seconds x 1000
|
||||
bus = dbus.SessionBus()
|
||||
notif = bus.get_object(self.item, self.path)
|
||||
notify = dbus.Interface(notif, self.interface)
|
||||
notify.Notify(self.app_name, self.id_num_to_replace, self.icon, title, text, actions_list, hint, time)
|
@@ -1,9 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import wx
|
||||
|
||||
class notification(object):
|
||||
|
||||
def notify(self, title, text):
|
||||
wx.NotificationMessage(title, text).Show()
|
@@ -5,6 +5,7 @@ import time
|
||||
import logging
|
||||
import webbrowser
|
||||
import wx
|
||||
import demoji
|
||||
import config
|
||||
import output
|
||||
import application
|
||||
@@ -472,11 +473,16 @@ class Session(base.baseSession):
|
||||
aliases = self.settings.get("user-aliases")
|
||||
if aliases == None:
|
||||
log.error("Aliases are not defined for this config spec.")
|
||||
return user.name
|
||||
return self.demoji_user(user.name)
|
||||
user_alias = aliases.get(user.id_str)
|
||||
if user_alias != None:
|
||||
return user_alias
|
||||
return user.name
|
||||
return self.demoji_user(user.name)
|
||||
|
||||
def demoji_user(self, name):
|
||||
if self.settings["general"]["hide_emojis"] == True:
|
||||
return demoji.replace(name, "")
|
||||
return name
|
||||
|
||||
def get_user_by_screen_name(self, screen_name):
|
||||
""" Returns an user identifier associated with a screen_name.
|
||||
@@ -570,11 +576,13 @@ class Session(base.baseSession):
|
||||
# the Streaming API sends non-extended tweets with an optional parameter "extended_tweets" which contains full_text and other data.
|
||||
# so we have to make sure we check it before processing the normal status.
|
||||
# As usual, we handle also quotes and retweets at first.
|
||||
if hasattr(status, "retweeted_status") and hasattr(status.retweeted_status, "quoted_status") and status.retweeted_status.quoted_status.truncated:
|
||||
status.retweeted_status.quoted_status._json = {**status.retweeted_status.quoted_status._json, **status.retweeted_status.quoted_status._json["extended_tweet"]}
|
||||
if hasattr(status, "retweeted_status") and hasattr(status.retweeted_status, "extended_tweet"):
|
||||
status.retweeted_status._json = {**status.retweeted_status._json, **status.retweeted_status._json["extended_tweet"]}
|
||||
# compose.compose_tweet requires the parent tweet to have a full_text field, so we have to add it to retweets here.
|
||||
status._json["full_text"] = status._json["text"]
|
||||
if hasattr(status, "quoted_status") and hasattr(status.quoted_status, "extended_tweet"):
|
||||
elif hasattr(status, "quoted_status") and hasattr(status.quoted_status, "extended_tweet"):
|
||||
status.quoted_status._json = {**status.quoted_status._json, **status.quoted_status._json["extended_tweet"]}
|
||||
if status.truncated:
|
||||
status._json = {**status._json, **status._json["extended_tweet"]}
|
||||
@@ -595,7 +603,7 @@ class Session(base.baseSession):
|
||||
num = self.order_buffer(buffer, [status])
|
||||
if num == 0:
|
||||
buffers_to_send.remove(buffer)
|
||||
# However, we have to do the "reduce and change" process here because the status we sent to the db is going to be a different object that the one sent to database.
|
||||
# However, we have to do the "reduce and change" process here because the status we sent to the db is going to be a different object that the one sent to controller.
|
||||
status = reduce.reduce_tweet(status)
|
||||
status = self.check_quoted_status(status)
|
||||
status = self.check_long_tweet(status)
|
||||
|
@@ -36,7 +36,7 @@ def process_text(tweet):
|
||||
# Replace URLS for extended version of those.
|
||||
if hasattr(tweet, "entities"):
|
||||
text = utils.expand_urls(text, tweet.entities)
|
||||
text = re.sub(r"https://twitter.com/\w+/status/\d+", "", text)
|
||||
text = re.sub(r"https://twitter.com/\w+/status/\S+", "", text)
|
||||
return text
|
||||
|
||||
def process_image_descriptions(entities):
|
||||
@@ -114,7 +114,7 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0):
|
||||
"""
|
||||
global dm_variables
|
||||
available_data = dict()
|
||||
available_data.update(text=utils.expand_urls(dm.message_create["message_data"]["text"], dm.message_create["message_data"]["entities"]))
|
||||
available_data.update(text=utils.expand_urls(utils.StripChars(dm.message_create["message_data"]["text"]), dm.message_create["message_data"]["entities"]))
|
||||
# Let's remove the last 3 digits in the timestamp string.
|
||||
# Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it.
|
||||
original_date = arrow.get(int(dm.created_timestamp))
|
||||
|
@@ -99,8 +99,13 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
||||
def __init__(self, parent):
|
||||
super(generalAccount, self).__init__(parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.au = wx.Button(self, wx.ID_ANY, _(u"Autocompletion settings..."))
|
||||
sizer.Add(self.au, 0, wx.ALL, 5)
|
||||
userAutocompletionBox = wx.StaticBox(self, label=_("User autocompletion settings"))
|
||||
self.userAutocompletionScan = wx.Button(self, wx.ID_ANY, _("Scan account and add friends and followers to the user autocompletion database"))
|
||||
self.userAutocompletionManage = wx.Button(self, wx.ID_ANY, _("Manage autocompletion database"))
|
||||
autocompletionSizer = wx.StaticBoxSizer(userAutocompletionBox, wx.HORIZONTAL)
|
||||
autocompletionSizer.Add(self.userAutocompletionScan, 0, wx.ALL, 5)
|
||||
autocompletionSizer.Add(self.userAutocompletionManage, 0, wx.ALL, 5)
|
||||
sizer.Add(autocompletionSizer, 0, wx.ALL, 5)
|
||||
self.relative_time = wx.CheckBox(self, wx.ID_ANY, _(U"Relative timestamps"))
|
||||
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
||||
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
@@ -120,6 +125,8 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
||||
sizer.Add(rMode, 0, wx.ALL, 5)
|
||||
self.show_screen_names = wx.CheckBox(self, wx.ID_ANY, _(U"Show screen names instead of full names"))
|
||||
sizer.Add(self.show_screen_names, 0, wx.ALL, 5)
|
||||
self.hide_emojis = wx.CheckBox(self, wx.ID_ANY, _("hide emojis in usernames"))
|
||||
sizer.Add(self.hide_emojis, 0, wx.ALL, 5)
|
||||
PersistSizeLabel = wx.StaticText(self, -1, _(u"Number of items per buffer to cache in database (0 to disable caching, blank for unlimited)"))
|
||||
self.persist_size = wx.TextCtrl(self, -1)
|
||||
sizer.Add(PersistSizeLabel, 0, wx.ALL, 5)
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6603
tools/twblue.pot
6603
tools/twblue.pot
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{"current_version": "2021.02.23",
|
||||
"description": "Snapshot version.",
|
||||
{"current_version": "2022.8.28",
|
||||
"description": "Added support for creating threads, upload videos and polls to Twitter.",
|
||||
"date": "unknown",
|
||||
"downloads":
|
||||
{"Windows32": "https://twblue.es/pubs/twblue_snapshot_x86.zip",
|
||||
{"Windows64": "https://twblue.es/pubs/twblue_snapshot_x64.zip"
|
||||
{"Windows32": "https://twblue.es/pubs/twblue_x86.zip",
|
||||
"Windows64": "https://twblue.es/pubs/twblue_x64.zip"}
|
||||
}
|
Reference in New Issue
Block a user