mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-26 09:59:23 +00:00
Compare commits
55 Commits
Author | SHA1 | Date | |
---|---|---|---|
97afc379e8 | |||
7f401ba789 | |||
ff22ae5653 | |||
02d94fcea0 | |||
df2015f360 | |||
64a14c831b | |||
fbbe7852c2 | |||
9dfccd2bd0 | |||
969a75e9f3 | |||
a59ba5ef78 | |||
3ebfdbc48b | |||
8db14a95c1 | |||
1b9062d86f | |||
4b60a79e49 | |||
002e1ccb55 | |||
0bcdf88290 | |||
0612c653b8 | |||
![]() |
c5dadb063a | ||
35d6010298 | |||
40a63d9e16 | |||
5712dd735b | |||
2c75ea5005 | |||
e35f37fcc2 | |||
71358ea74d | |||
b8f822830f | |||
74e4fe6357 | |||
77bee64421 | |||
c761230566 | |||
49505fabcd | |||
4ad01d7833 | |||
ab1a13f886 | |||
![]() |
44c25e54f8 | ||
cdabd6f055 | |||
60144a6b08 | |||
382acf7c8c | |||
![]() |
03ba59028f | ||
![]() |
50125fc55a | ||
39e1fb017c | |||
2aaa4eced3 | |||
![]() |
6d2eac5b1c | ||
![]() |
40040d1b17 | ||
2a791d43bf | |||
b10aeb046d | |||
7d6e230fd9 | |||
9346bba7a0 | |||
30f739c42e | |||
eb0679cb96 | |||
45deae3402 | |||
5b0b26799d | |||
ee234b80a7 | |||
0065af2aef | |||
9c086cfa0f | |||
2f263a23b7 | |||
9cb6eafbbc | |||
![]() |
0111c8aae1 |
@@ -39,5 +39,5 @@ florian Ionașcu
|
|||||||
Christian Leo Mameli
|
Christian Leo Mameli
|
||||||
Natalia Hedlund (Наталья Хедлунд)
|
Natalia Hedlund (Наталья Хедлунд)
|
||||||
Valeria (Валерия)
|
Valeria (Валерия)
|
||||||
Corentin Bacqué-Cazenave
|
Oreonan
|
||||||
Artem Plaksin (maniyax)
|
Artem Plaksin (maniyax)
|
@@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
## changes in this version
|
## changes in this version
|
||||||
|
|
||||||
|
* Added support for Twitter audio and videos: Tweets which contains audio or videos will be detected as audio items, and you can playback those with the regular command to play audios. ([#384,](https://github.com/manuelcortez/TWBlue/pull/384))
|
||||||
|
* We just implemented some changes in the way TWBlue handles tweets in order to reduce its RAM memory usage [#380](https://github.com/manuelcortez/TWBlue/pull/380):
|
||||||
|
* We reduced the tweets size by storing only the tweet fields we currently use. This should reduce tweet's size in memory for every object up to 75%.
|
||||||
|
* When using the cache database to store your tweets, there is a new setting present in the account settings dialog, in the general tab. This setting allows you to control whether TWBlue will load the whole database into memory (which is the current behaviour) or not.
|
||||||
|
* Loading the whole database into memory has the advantage of being extremely fast to access any element (for example when moving through tweets in a buffer), but it requires more memory as the tweet buffers grow up. This should, however, use less memory than before thanks to the optimizations performed in tweet objects. If you have a machine with enough memory, this should be a good option for your case.
|
||||||
|
* If you uncheck this setting, TWBlue will read the whole database from disk. This is significantly slower, but the advantage of this setting is that it will consume almost no extra memory, no matter how big is the tweets dataset. Be ware, though, that TWBlue might start to feel slower when accessing elements (for example when reading tweets) as the buffers grow up. This setting is suggested for computers with low memory or for those people not wanting to keep a really big amount of tweets stored.
|
||||||
|
* Changed the label in the direct message's text control so it will indicate that the user needs to write the text there, without referring to any username in particular. ([#366,](https://github.com/manuelcortez/TWBlue/issues/366))
|
||||||
|
* TWBlue will take Shift+F10 again as the contextual menu key in the list of items in a buffer. This stopped working after we have migrated to WX 4.1. ([#353,](https://github.com/manuelcortez/TWBlue/issues/353))
|
||||||
|
* TWBlue should render correctly retweets of quoted tweets. ([#365,](https://github.com/manuelcortez/TWBlue/issues/365))
|
||||||
|
* Fixed an error that was causing TWBlue to be unable to output to screen readers at times. ([#369,](https://github.com/manuelcortez/TWBlue/issues/369))
|
||||||
|
* Fixed autocomplete users feature. ([#367,](https://github.com/manuelcortez/TWBlue/issues/367))
|
||||||
* Fixed error when displaying an URL at the end of a line, when the tweet or direct message contained multiple lines. Now the URL should be displayed correctly. ([#305,](https://github.com/manuelcortez/TWBlue/issues/305) [#272,](https://github.com/manuelcortez/TWBlue/issues/272))
|
* Fixed error when displaying an URL at the end of a line, when the tweet or direct message contained multiple lines. Now the URL should be displayed correctly. ([#305,](https://github.com/manuelcortez/TWBlue/issues/305) [#272,](https://github.com/manuelcortez/TWBlue/issues/272))
|
||||||
* TWBlue has been migrated completely to Python 3 (currently, the software builds with Python 3.8).
|
* TWBlue has been migrated completely to Python 3 (currently, the software builds with Python 3.8).
|
||||||
* TWBlue should be restarted gracefully. Before, the application was alerting users of not being closed properly every time the application restarted by itself.
|
* TWBlue should be restarted gracefully. Before, the application was alerting users of not being closed properly every time the application restarted by itself.
|
||||||
|
@@ -16,9 +16,9 @@ def prepare_documentation_in_file(fileSource, fileDest):
|
|||||||
if "\n" == i:
|
if "\n" == i:
|
||||||
newvar = "\"\","
|
newvar = "\"\","
|
||||||
elif "\n" == i[-1]:
|
elif "\n" == i[-1]:
|
||||||
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i[:-1])
|
newvar = "\"\"\"%s\"\"\",\n" % (i[:-1])
|
||||||
else:
|
else:
|
||||||
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i)
|
newvar = "\"\"\"%s\"\"\",\n" % (i)
|
||||||
f2.write(newvar)
|
f2.write(newvar)
|
||||||
f1.close()
|
f1.close()
|
||||||
f2.write("]")
|
f2.write("]")
|
||||||
|
@@ -8,28 +8,27 @@ import shutil
|
|||||||
from codecs import open as _open
|
from codecs import open as _open
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
|
|
||||||
def change_language(name, language):
|
def get_translation_function(name, language):
|
||||||
global _
|
if language == "en":
|
||||||
os.environ["lang"] = language
|
return gettext.NullTranslations()
|
||||||
_ = gettext.install(name, os.path.join(paths.app_path(), "locales"))
|
translation_function = gettext.translation(name, os.path.join(paths.app_path(), "locales"), languages=[language])
|
||||||
|
return translation_function
|
||||||
|
|
||||||
# the list of supported language codes of TW Blue
|
# the list of supported language codes of TW Blue
|
||||||
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da"]
|
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da", "sr"]
|
||||||
|
|
||||||
def generate_document(language, document_type="documentation"):
|
def generate_document(language, document_type="documentation"):
|
||||||
if document_type == "documentation":
|
if document_type == "documentation":
|
||||||
translation_file = "twblue-documentation"
|
translation_file = "twblue-documentation"
|
||||||
change_language(translation_file, language)
|
translation_function = get_translation_function(translation_file, language)
|
||||||
reload(strings)
|
markdown_file = markdown.markdown("\n".join([translation_function.gettext(s[:-1]) if s != "\n" else s for s in strings.documentation[1:]]), extensions=["markdown.extensions.toc"])
|
||||||
markdown_file = markdown.markdown("\n".join(strings.documentation[1:]), extensions=["markdown.extensions.toc"])
|
title = translation_function.gettext(strings.documentation[0][:-1])
|
||||||
title = strings.documentation[0]
|
|
||||||
filename = "manual.html"
|
filename = "manual.html"
|
||||||
elif document_type == "changelog":
|
elif document_type == "changelog":
|
||||||
translation_file = "twblue-changelog"
|
translation_file = "twblue-changelog"
|
||||||
change_language(translation_file, language)
|
translation_function = get_translation_function(translation_file, language)
|
||||||
reload(changelog)
|
markdown_file = markdown.markdown("\n".join([translation_function.gettext(s[:-1]) if s != "\n" else s for s in changelog.documentation[1:]]), extensions=["markdown.extensions.toc"])
|
||||||
markdown_file = markdown.markdown("\n".join(changelog.documentation[1:]), extensions=["markdown.extensions.toc"])
|
title = translation_function.gettext(changelog.documentation[0][:-1])
|
||||||
title = changelog.documentation[0]
|
|
||||||
filename = "changelog.html"
|
filename = "changelog.html"
|
||||||
first_html_block = """<!doctype html>
|
first_html_block = """<!doctype html>
|
||||||
<html lang="%s">
|
<html lang="%s">
|
||||||
@@ -56,11 +55,13 @@ def create_documentation():
|
|||||||
shutil.copy(os.path.join("..", "license.txt"), os.path.join("documentation", "license.txt"))
|
shutil.copy(os.path.join("..", "license.txt"), os.path.join("documentation", "license.txt"))
|
||||||
for i in languages:
|
for i in languages:
|
||||||
print("Creating documentation for: %s" % (i,))
|
print("Creating documentation for: %s" % (i,))
|
||||||
|
try:
|
||||||
generate_document(i)
|
generate_document(i)
|
||||||
generate_document(i, "changelog")
|
generate_document(i, "changelog")
|
||||||
|
except:
|
||||||
|
continue
|
||||||
print("Done")
|
print("Done")
|
||||||
|
|
||||||
change_language("twblue-documentation", "en")
|
|
||||||
import strings
|
import strings
|
||||||
import changelog
|
import changelog
|
||||||
create_documentation()
|
create_documentation()
|
BIN
doc/locales/sr/lc_messages/twblue-changelog.mo
Normal file
BIN
doc/locales/sr/lc_messages/twblue-changelog.mo
Normal file
Binary file not shown.
1309
doc/locales/sr/lc_messages/twblue-changelog.po
Normal file
1309
doc/locales/sr/lc_messages/twblue-changelog.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
doc/locales/sr/lc_messages/twblue-documentation.mo
Normal file
BIN
doc/locales/sr/lc_messages/twblue-documentation.mo
Normal file
Binary file not shown.
1885
doc/locales/sr/lc_messages/twblue-documentation.po
Normal file
1885
doc/locales/sr/lc_messages/twblue-documentation.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,8 @@ cx_freeze
|
|||||||
tweepy
|
tweepy
|
||||||
twitter-text-parser
|
twitter-text-parser
|
||||||
pyenchant
|
pyenchant
|
||||||
git+https://github.com/manuelcortez/libloader
|
sqlitedict
|
||||||
git+https://github.com/manuelcortez/platform_utils
|
git+https://github.com/accessibleapps/libloader
|
||||||
git+https://github.com/manuelcortez/accessible_output2
|
git+https://github.com/accessibleapps/platform_utils
|
||||||
git+https://github.com/jmdaweb/sound_lib
|
git+https://github.com/accessibleapps/accessible_output2
|
||||||
|
git+https://github.com/accessibleapps/sound_lib
|
@@ -12,6 +12,7 @@ reverse_timelines = boolean(default=False)
|
|||||||
announce_stream_status = boolean(default=True)
|
announce_stream_status = boolean(default=True)
|
||||||
retweet_mode = string(default="ask")
|
retweet_mode = string(default="ask")
|
||||||
persist_size = integer(default=0)
|
persist_size = integer(default=0)
|
||||||
|
load_cache_in_memory=boolean(default=True)
|
||||||
show_screen_names = boolean(default=False)
|
show_screen_names = boolean(default=False)
|
||||||
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted','events'))
|
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted','events'))
|
||||||
|
|
||||||
|
@@ -9,14 +9,14 @@ if snapshot == False:
|
|||||||
update_url = 'https://twblue.es/updates/stable.php'
|
update_url = 'https://twblue.es/updates/stable.php'
|
||||||
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
|
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
|
||||||
else:
|
else:
|
||||||
version = "4"
|
version = "6"
|
||||||
update_url = 'https://twblue.es/updates/snapshot.php'
|
update_url = 'https://twblue.es/updates/snapshot.php'
|
||||||
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
|
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
|
||||||
authors = ["Manuel Cortéz", "José Manuel Delicado"]
|
authors = ["Manuel Cortéz", "José Manuel Delicado"]
|
||||||
authorEmail = "manuel@manuelcortez.net"
|
authorEmail = "manuel@manuelcortez.net"
|
||||||
copyright = "Copyright (C) 2013-2021, Manuel cortéz."
|
copyright = "Copyright (C) 2013-2021, Manuel cortéz."
|
||||||
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."
|
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)", "Rémy Ruiz (French)", "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)"]
|
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)", "Oreonan (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 = u"https://twblue.es"
|
url = u"https://twblue.es"
|
||||||
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
|
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
|
||||||
supported_languages = []
|
supported_languages = []
|
@@ -59,11 +59,17 @@ class buffer(object):
|
|||||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
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 and ev.ShiftDown(): event = "clear_list"
|
||||||
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
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:
|
else:
|
||||||
event = None
|
event = None
|
||||||
ev.Skip()
|
ev.Skip()
|
||||||
if event != None:
|
if event != None:
|
||||||
try:
|
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)()
|
getattr(self, event)()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
@@ -19,7 +19,7 @@ import languageHandler
|
|||||||
import logging
|
import logging
|
||||||
from audio_services import youtube_utils
|
from audio_services import youtube_utils
|
||||||
from controller.buffers import baseBuffers
|
from controller.buffers import baseBuffers
|
||||||
from sessions.twitter import compose, utils
|
from sessions.twitter import compose, utils, reduce
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.error import TweepError
|
from tweepy.error import TweepError
|
||||||
from tweepy.cursor import Cursor
|
from tweepy.cursor import Cursor
|
||||||
@@ -178,7 +178,9 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
val, cursor = val
|
val, cursor = val
|
||||||
if type(cursor) == tuple:
|
if type(cursor) == tuple:
|
||||||
cursor = cursor[1]
|
cursor = cursor[1]
|
||||||
self.session.db["cursors"][self.name] = cursor
|
cursors = self.session.db["cursors"]
|
||||||
|
cursors[self.name] = cursor
|
||||||
|
self.session.db["cursors"] = cursors
|
||||||
results = [i for i in val]
|
results = [i for i in val]
|
||||||
val = results
|
val = results
|
||||||
val.reverse()
|
val.reverse()
|
||||||
@@ -190,7 +192,6 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
return
|
return
|
||||||
number_of_items = self.session.order_buffer(self.name, val)
|
number_of_items = self.session.order_buffer(self.name, val)
|
||||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||||
|
|
||||||
self.put_items_on_list(number_of_items)
|
self.put_items_on_list(number_of_items)
|
||||||
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
||||||
if "-timeline" in self.name:
|
if "-timeline" in self.name:
|
||||||
@@ -229,15 +230,19 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
return
|
return
|
||||||
if items == None:
|
if items == None:
|
||||||
return
|
return
|
||||||
|
items_db = self.session.db[self.name]
|
||||||
|
self.session.add_users_from_results(items)
|
||||||
for i in items:
|
for i in items:
|
||||||
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i.id, self.session.db[self.name]) == None:
|
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i.id, self.session.db[self.name]) == None:
|
||||||
|
i = reduce.reduce_tweet(i)
|
||||||
i = self.session.check_quoted_status(i)
|
i = self.session.check_quoted_status(i)
|
||||||
i = self.session.check_long_tweet(i)
|
i = self.session.check_long_tweet(i)
|
||||||
elements.append(i)
|
elements.append(i)
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
self.session.db[self.name].insert(0, i)
|
items_db.insert(0, i)
|
||||||
else:
|
else:
|
||||||
self.session.db[self.name].append(i)
|
items_db.append(i)
|
||||||
|
self.session.db[self.name] = items_db
|
||||||
selection = self.buffer.list.get_selected()
|
selection = self.buffer.list.get_selected()
|
||||||
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
|
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
@@ -286,10 +291,12 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
|
|
||||||
def remove_tweet(self, id):
|
def remove_tweet(self, id):
|
||||||
if type(self.session.db[self.name]) == dict: return
|
if type(self.session.db[self.name]) == dict: return
|
||||||
for i in range(0, len(self.session.db[self.name])):
|
items = self.session.db[self.name]
|
||||||
if self.session.db[self.name][i].id == id:
|
for i in range(0, len(items)):
|
||||||
self.session.db[self.name].pop(i)
|
if items[i].id == id:
|
||||||
|
items.pop(i)
|
||||||
self.remove_item(i)
|
self.remove_item(i)
|
||||||
|
self.session.db[self.name] = items
|
||||||
|
|
||||||
def put_items_on_list(self, number_of_items):
|
def put_items_on_list(self, number_of_items):
|
||||||
list_to_use = self.session.db[self.name]
|
list_to_use = self.session.db[self.name]
|
||||||
@@ -408,11 +415,12 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
@_tweets_exist
|
@_tweets_exist
|
||||||
def reply(self, *args, **kwargs):
|
def reply(self, *args, **kwargs):
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
screen_name = tweet.user.screen_name
|
user = self.session.get_user(tweet.user)
|
||||||
|
screen_name = user.screen_name
|
||||||
id = tweet.id
|
id = tweet.id
|
||||||
twishort_enabled = hasattr(tweet, "twishort")
|
twishort_enabled = hasattr(tweet, "twishort")
|
||||||
users = utils.get_all_mentioned(tweet, self.session.db, field="screen_name")
|
users = utils.get_all_mentioned(tweet, self.session.db, field="screen_name")
|
||||||
ids = utils.get_all_mentioned(tweet, self.session.db, field="id_str")
|
ids = utils.get_all_mentioned(tweet, self.session.db, field="id")
|
||||||
# Build the window title
|
# Build the window title
|
||||||
if len(users) < 1:
|
if len(users) < 1:
|
||||||
title=_("Reply to {arg0}").format(arg0=screen_name)
|
title=_("Reply to {arg0}").format(arg0=screen_name)
|
||||||
@@ -461,8 +469,8 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
screen_name = tweet.screen_name
|
screen_name = tweet.screen_name
|
||||||
users = [screen_name]
|
users = [screen_name]
|
||||||
else:
|
else:
|
||||||
screen_name = tweet.user.screen_name
|
screen_name = self.session.get_user(tweet.user).screen_name
|
||||||
users = utils.get_all_users(tweet, self.session.db)
|
users = utils.get_all_users(tweet, self.session)
|
||||||
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
|
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
|
||||||
if dm.message.get_response() == widgetUtils.OK:
|
if dm.message.get_response() == widgetUtils.OK:
|
||||||
screen_name = dm.message.get("cb")
|
screen_name = dm.message.get("cb")
|
||||||
@@ -471,10 +479,12 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
text = dm.message.get_text()
|
text = dm.message.get_text()
|
||||||
val = self.session.api_call(call_name="send_direct_message", recipient_id=recipient_id, text=text)
|
val = self.session.api_call(call_name="send_direct_message", recipient_id=recipient_id, text=text)
|
||||||
if val != None:
|
if val != None:
|
||||||
|
sent_dms = self.session.db["sent_direct_messages"]
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
self.session.db["sent_direct_messages"].append(val)
|
sent_dms.append(val)
|
||||||
else:
|
else:
|
||||||
self.session.db["sent_direct_messages"].insert(0, val)
|
sent_dms.insert(0, val)
|
||||||
|
self.session.db["sent_direct_messages"] = sent_dms
|
||||||
pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"])
|
pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"])
|
||||||
if hasattr(dm.message, "destroy"): dm.message.destroy()
|
if hasattr(dm.message, "destroy"): dm.message.destroy()
|
||||||
|
|
||||||
@@ -501,12 +511,12 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
comments = tweet.full_text
|
comments = tweet.full_text
|
||||||
else:
|
else:
|
||||||
comments = tweet.text
|
comments = tweet.text
|
||||||
retweet = messages.tweet(self.session, _(u"Quote"), _(u"Add your comment to the tweet"), u"“@%s: %s ”" % (tweet.user.screen_name, comments), max=256, messageType="retweet")
|
retweet = messages.tweet(self.session, _(u"Quote"), _(u"Add your comment to the tweet"), u"“@%s: %s ”" % (self.session.get_user(tweet.user).screen_name, comments), max=256, messageType="retweet")
|
||||||
if comment != '':
|
if comment != '':
|
||||||
retweet.message.set_text(comment)
|
retweet.message.set_text(comment)
|
||||||
if retweet.message.get_response() == widgetUtils.OK:
|
if retweet.message.get_response() == widgetUtils.OK:
|
||||||
text = retweet.message.get_text()
|
text = retweet.message.get_text()
|
||||||
text = text+" https://twitter.com/{0}/status/{1}".format(tweet.user.screen_name, id)
|
text = text+" https://twitter.com/{0}/status/{1}".format(self.session.get_user(tweet.user).screen_name, id)
|
||||||
if retweet.image == None:
|
if retweet.image == None:
|
||||||
item = self.session.api_call(call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id, tweet_mode="extended")
|
item = self.session.api_call(call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id, tweet_mode="extended")
|
||||||
if item != None:
|
if item != None:
|
||||||
@@ -543,7 +553,7 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
return sound.URLPlayer.stop_audio()
|
return sound.URLPlayer.stop_audio()
|
||||||
tweet = self.get_tweet()
|
tweet = self.get_tweet()
|
||||||
if tweet == None: return
|
if tweet == None: return
|
||||||
urls = utils.find_urls(tweet)
|
urls = utils.find_urls(tweet, twitter_media=True)
|
||||||
if len(urls) == 1:
|
if len(urls) == 1:
|
||||||
url=urls[0]
|
url=urls[0]
|
||||||
elif len(urls) > 1:
|
elif len(urls) > 1:
|
||||||
@@ -588,16 +598,18 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
|
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
|
||||||
answer = commonMessageDialogs.delete_tweet_dialog(None)
|
answer = commonMessageDialogs.delete_tweet_dialog(None)
|
||||||
if answer == widgetUtils.YES:
|
if answer == widgetUtils.YES:
|
||||||
|
items = self.session.db[self.name]
|
||||||
try:
|
try:
|
||||||
if self.name == "direct_messages" or self.name == "sent_direct_messages":
|
if self.name == "direct_messages" or self.name == "sent_direct_messages":
|
||||||
self.session.twitter.destroy_direct_message(id=self.get_right_tweet().id)
|
self.session.twitter.destroy_direct_message(id=self.get_right_tweet().id)
|
||||||
self.session.db[self.name].pop(index)
|
items.pop(index)
|
||||||
else:
|
else:
|
||||||
self.session.twitter.destroy_status(id=self.get_right_tweet().id)
|
self.session.twitter.destroy_status(id=self.get_right_tweet().id)
|
||||||
self.session.db[self.name].pop(index)
|
items.pop(index)
|
||||||
self.buffer.list.remove_item(index)
|
self.buffer.list.remove_item(index)
|
||||||
except TweepError:
|
except TweepError:
|
||||||
self.session.sound.play("error.ogg")
|
self.session.sound.play("error.ogg")
|
||||||
|
self.session.db[self.name] = items
|
||||||
|
|
||||||
@_tweets_exist
|
@_tweets_exist
|
||||||
def user_details(self):
|
def user_details(self):
|
||||||
@@ -607,7 +619,7 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
elif self.type == "people":
|
elif self.type == "people":
|
||||||
users = [tweet.screen_name]
|
users = [tweet.screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, self.session.db)
|
users = utils.get_all_users(tweet, self.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
|
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
user.profileController(session=self.session, user=dlg.get_user())
|
user.profileController(session=self.session, user=dlg.get_user())
|
||||||
@@ -625,7 +637,7 @@ class baseBufferController(baseBuffers.buffer):
|
|||||||
def open_in_browser(self, *args, **kwargs):
|
def open_in_browser(self, *args, **kwargs):
|
||||||
tweet = self.get_tweet()
|
tweet = self.get_tweet()
|
||||||
output.speak(_(u"Opening item in web browser..."))
|
output.speak(_(u"Opening item in web browser..."))
|
||||||
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=tweet.user.screen_name, tweet_id=tweet.id)
|
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=self.session.get_user(tweet.user).screen_name, tweet_id=tweet.id)
|
||||||
webbrowser.open(url)
|
webbrowser.open(url)
|
||||||
|
|
||||||
class directMessagesController(baseBufferController):
|
class directMessagesController(baseBufferController):
|
||||||
@@ -646,7 +658,9 @@ class directMessagesController(baseBufferController):
|
|||||||
items, cursor = items
|
items, cursor = items
|
||||||
if type(cursor) == tuple:
|
if type(cursor) == tuple:
|
||||||
cursor = cursor[1]
|
cursor = cursor[1]
|
||||||
self.session.db["cursors"][self.name] = cursor
|
cursors = self.session.db["cursors"]
|
||||||
|
cursors[self.name] = cursor
|
||||||
|
self.session.db["cursors"] = cursors
|
||||||
results = [i for i in items]
|
results = [i for i in items]
|
||||||
items = results
|
items = results
|
||||||
log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function))
|
log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function))
|
||||||
@@ -657,22 +671,26 @@ class directMessagesController(baseBufferController):
|
|||||||
return
|
return
|
||||||
sent = []
|
sent = []
|
||||||
received = []
|
received = []
|
||||||
|
sent_dms = self.session.db["sent_direct_messages"]
|
||||||
|
received_dms = self.session.db["direct_messages"]
|
||||||
for i in items:
|
for i in items:
|
||||||
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
|
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
self.session.db["sent_direct_messages"].insert(0, i)
|
sent_dms.insert(0, i)
|
||||||
sent.append(i)
|
sent.append(i)
|
||||||
else:
|
else:
|
||||||
self.session.db["sent_direct_messages"].append(i)
|
sent_dms.append(i)
|
||||||
sent.insert(0, i)
|
sent.insert(0, i)
|
||||||
else:
|
else:
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
self.session.db[self.name].insert(0, i)
|
received_dms.insert(0, i)
|
||||||
received.append(i)
|
received.append(i)
|
||||||
else:
|
else:
|
||||||
self.session.db[self.name].append(i)
|
received_dms.append(i)
|
||||||
received.insert(0, i)
|
received.insert(0, i)
|
||||||
total = total+1
|
total = total+1
|
||||||
|
self.session.db["direct_messages"] = received_dms
|
||||||
|
self.session.db["sent_direct_messages"] = sent_dms
|
||||||
user_ids = [item.message_create["sender_id"] for item in items]
|
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("more-sent-dms", data=sent, account=self.session.db["user_name"])
|
||||||
@@ -885,7 +903,9 @@ class peopleBufferController(baseBufferController):
|
|||||||
val, cursor = val
|
val, cursor = val
|
||||||
if type(cursor) == tuple:
|
if type(cursor) == tuple:
|
||||||
cursor = cursor[1]
|
cursor = cursor[1]
|
||||||
self.session.db["cursors"][self.name] = cursor
|
cursors = self.session.db["cursors"]
|
||||||
|
cursors[self.name] = cursor
|
||||||
|
self.session.db["cursors"] = cursors
|
||||||
results = [i for i in val]
|
results = [i for i in val]
|
||||||
val = results
|
val = results
|
||||||
val.reverse()
|
val.reverse()
|
||||||
@@ -914,7 +934,9 @@ class peopleBufferController(baseBufferController):
|
|||||||
items, cursor = items
|
items, cursor = items
|
||||||
if type(cursor) == tuple:
|
if type(cursor) == tuple:
|
||||||
cursor = cursor[1]
|
cursor = cursor[1]
|
||||||
self.session.db["cursors"][self.name] = cursor
|
cursors = self.session.db["cursors"]
|
||||||
|
cursors[self.name] = cursor
|
||||||
|
self.session.db["cursors"] = cursors
|
||||||
results = [i for i in items]
|
results = [i for i in items]
|
||||||
items = results
|
items = results
|
||||||
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
|
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
|
||||||
@@ -923,11 +945,13 @@ class peopleBufferController(baseBufferController):
|
|||||||
return
|
return
|
||||||
if items == None:
|
if items == None:
|
||||||
return
|
return
|
||||||
|
items_db = self.session.db[self.name]
|
||||||
for i in items:
|
for i in items:
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
self.session.db[self.name].insert(0, i)
|
items_db.insert(0, i)
|
||||||
else:
|
else:
|
||||||
self.session.db[self.name].append(i)
|
items_db.append(i)
|
||||||
|
self.session.db[self.name] = items_db
|
||||||
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 items:
|
for i in items:
|
||||||
@@ -1206,6 +1230,8 @@ class conversationBufferController(searchBufferController):
|
|||||||
self.statuses.append(self.tweet)
|
self.statuses.append(self.tweet)
|
||||||
self.ids.append(self.tweet.id)
|
self.ids.append(self.tweet.id)
|
||||||
tweet = self.tweet
|
tweet = self.tweet
|
||||||
|
if not hasattr(tweet, "in_reply_to_status_id"):
|
||||||
|
tweet.in_reply_to_status_id = None
|
||||||
while tweet.in_reply_to_status_id != None:
|
while tweet.in_reply_to_status_id != None:
|
||||||
try:
|
try:
|
||||||
tweet = self.session.twitter.get_status(id=tweet.in_reply_to_status_id, tweet_mode="extended")
|
tweet = self.session.twitter.get_status(id=tweet.in_reply_to_status_id, tweet_mode="extended")
|
||||||
|
@@ -253,9 +253,9 @@ class Controller(object):
|
|||||||
|
|
||||||
# Connection checker executed each minute.
|
# Connection checker executed each minute.
|
||||||
self.checker_function = RepeatingTimer(60, self.check_connection)
|
self.checker_function = RepeatingTimer(60, self.check_connection)
|
||||||
self.checker_function.start()
|
# self.checker_function.start()
|
||||||
self.save_db = RepeatingTimer(300, self.save_data_in_db)
|
# self.save_db = RepeatingTimer(300, self.save_data_in_db)
|
||||||
self.save_db.start()
|
# self.save_db.start()
|
||||||
log.debug("Setting updates to buffers every %d seconds..." % (60*config.app["app-settings"]["update_period"],))
|
log.debug("Setting updates to buffers every %d seconds..." % (60*config.app["app-settings"]["update_period"],))
|
||||||
self.update_buffers_function = RepeatingTimer(60*config.app["app-settings"]["update_period"], self.update_buffers)
|
self.update_buffers_function = RepeatingTimer(60*config.app["app-settings"]["update_period"], self.update_buffers)
|
||||||
self.update_buffers_function.start()
|
self.update_buffers_function.start()
|
||||||
@@ -530,7 +530,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
user = dlg.get_user()
|
user = dlg.get_user()
|
||||||
@@ -547,7 +547,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
user = dlg.get_user()
|
user = dlg.get_user()
|
||||||
@@ -575,7 +575,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
user = dlg.get_user()
|
user = dlg.get_user()
|
||||||
@@ -617,6 +617,7 @@ class Controller(object):
|
|||||||
if d.needs_restart == True:
|
if d.needs_restart == True:
|
||||||
commonMessageDialogs.needs_restart()
|
commonMessageDialogs.needs_restart()
|
||||||
buff.session.settings.write()
|
buff.session.settings.write()
|
||||||
|
buff.session.save_persistent_data()
|
||||||
restart.restart_program()
|
restart.restart_program()
|
||||||
|
|
||||||
def report_error(self, *args, **kwargs):
|
def report_error(self, *args, **kwargs):
|
||||||
@@ -651,8 +652,8 @@ class Controller(object):
|
|||||||
if sessions.sessions[item].logged == False: continue
|
if sessions.sessions[item].logged == False: continue
|
||||||
log.debug("Disconnecting streams for %s session" % (sessions.sessions[item].session_id,))
|
log.debug("Disconnecting streams for %s session" % (sessions.sessions[item].session_id,))
|
||||||
sessions.sessions[item].sound.cleaner.cancel()
|
sessions.sessions[item].sound.cleaner.cancel()
|
||||||
log.debug("Shelving database for " + sessions.sessions[item].session_id)
|
log.debug("Saving database for " + sessions.sessions[item].session_id)
|
||||||
sessions.sessions[item].shelve()
|
sessions.sessions[item].save_persistent_data()
|
||||||
if system == "Windows":
|
if system == "Windows":
|
||||||
self.systrayIcon.RemoveIcon()
|
self.systrayIcon.RemoveIcon()
|
||||||
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
||||||
@@ -669,7 +670,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users)
|
u = userActionsController.userActionsController(buff, users)
|
||||||
|
|
||||||
def unfollow(self, *args, **kwargs):
|
def unfollow(self, *args, **kwargs):
|
||||||
@@ -681,7 +682,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users, "unfollow")
|
u = userActionsController.userActionsController(buff, users, "unfollow")
|
||||||
|
|
||||||
def mute(self, *args, **kwargs):
|
def mute(self, *args, **kwargs):
|
||||||
@@ -693,7 +694,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users, "mute")
|
u = userActionsController.userActionsController(buff, users, "mute")
|
||||||
|
|
||||||
def unmute(self, *args, **kwargs):
|
def unmute(self, *args, **kwargs):
|
||||||
@@ -705,7 +706,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users, "unmute")
|
u = userActionsController.userActionsController(buff, users, "unmute")
|
||||||
|
|
||||||
def block(self, *args, **kwargs):
|
def block(self, *args, **kwargs):
|
||||||
@@ -717,7 +718,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users, "block")
|
u = userActionsController.userActionsController(buff, users, "block")
|
||||||
|
|
||||||
def unblock(self, *args, **kwargs):
|
def unblock(self, *args, **kwargs):
|
||||||
@@ -729,7 +730,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users, "unblock")
|
u = userActionsController.userActionsController(buff, users, "unblock")
|
||||||
|
|
||||||
def report(self, *args, **kwargs):
|
def report(self, *args, **kwargs):
|
||||||
@@ -741,7 +742,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
u = userActionsController.userActionsController(buff, users, "report")
|
u = userActionsController.userActionsController(buff, users, "report")
|
||||||
|
|
||||||
def post_tweet(self, event=None):
|
def post_tweet(self, event=None):
|
||||||
@@ -828,7 +829,7 @@ class Controller(object):
|
|||||||
elif buff.type == "dm":
|
elif buff.type == "dm":
|
||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session.db)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
|
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
usr = utils.if_user_exists(buff.session.twitter, dlg.get_user())
|
usr = utils.if_user_exists(buff.session.twitter, dlg.get_user())
|
||||||
@@ -923,8 +924,8 @@ class Controller(object):
|
|||||||
|
|
||||||
def open_conversation(self, *args, **kwargs):
|
def open_conversation(self, *args, **kwargs):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
id = buffer.get_right_tweet().id_str
|
id = buffer.get_right_tweet().id
|
||||||
user = buffer.get_right_tweet().user.screen_name
|
user = buffer.session.get_user(buffer.get_right_tweet().user).screen_name
|
||||||
search = twitterBuffers.conversationBufferController(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
|
search = twitterBuffers.conversationBufferController(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
|
||||||
search.tweet = buffer.get_right_tweet()
|
search.tweet = buffer.get_right_tweet()
|
||||||
search.start_stream(start=True)
|
search.start_stream(start=True)
|
||||||
@@ -1274,10 +1275,12 @@ class Controller(object):
|
|||||||
data = buffer.session.check_long_tweet(data)
|
data = buffer.session.check_long_tweet(data)
|
||||||
if data == False: # Long tweet deleted from twishort.
|
if data == False: # Long tweet deleted from twishort.
|
||||||
return
|
return
|
||||||
|
items = buffer.session.db[buffer.name]
|
||||||
if buffer.session.settings["general"]["reverse_timelines"] == False:
|
if buffer.session.settings["general"]["reverse_timelines"] == False:
|
||||||
buffer.session.db[buffer.name].append(data)
|
items.append(data)
|
||||||
else:
|
else:
|
||||||
buffer.session.db[buffer.name].insert(0, data)
|
items.insert(0, data)
|
||||||
|
buffer.session.db[buffer.name] = items
|
||||||
buffer.add_new_item(data)
|
buffer.add_new_item(data)
|
||||||
|
|
||||||
def manage_friend(self, data, user):
|
def manage_friend(self, data, user):
|
||||||
@@ -1326,7 +1329,11 @@ class Controller(object):
|
|||||||
i.start_stream()
|
i.start_stream()
|
||||||
else:
|
else:
|
||||||
i.start_stream(play_sound=False)
|
i.start_stream(play_sound=False)
|
||||||
except TweepError:
|
except TweepError as err:
|
||||||
|
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r due to the following reason: %s" % (err.api_code, i.name, i.account, i.args, i.kwargs, err.reason))
|
||||||
|
# Determine if this error was caused by a block applied to the current user (IE permission errors).
|
||||||
|
errors_allowed = [130]
|
||||||
|
if (err.api_code != None and err.api_code not in errors_allowed) or (err.api_code == None and 'Not authorized' in err.reason): # A twitter error, so safely try to remove the buffer.
|
||||||
buff = self.view.search(i.name, i.account)
|
buff = self.view.search(i.name, i.account)
|
||||||
i.remove_buffer(force=True)
|
i.remove_buffer(force=True)
|
||||||
commonMessageDialogs.blocked_timeline()
|
commonMessageDialogs.blocked_timeline()
|
||||||
@@ -1532,7 +1539,11 @@ class Controller(object):
|
|||||||
if i.session != None and i.session.is_logged == True:
|
if i.session != None and i.session.is_logged == True:
|
||||||
try:
|
try:
|
||||||
i.start_stream(mandatory=True)
|
i.start_stream(mandatory=True)
|
||||||
except TweepError:
|
except TweepError as err:
|
||||||
|
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r due to the following reason: %s" % (err.api_code, i.name, i.account, i.args, i.kwargs, err.reason))
|
||||||
|
# Determine if this error was caused by a block applied to the current user (IE permission errors).
|
||||||
|
errors_allowed = [130]
|
||||||
|
if (err.api_code != None and err.api_code not in errors_allowed) or (err.api_code == None and 'Not authorized' in err.reason): # A twitter error, so safely try to remove the buffer.
|
||||||
buff = self.view.search(i.name, i.account)
|
buff = self.view.search(i.name, i.account)
|
||||||
i.remove_buffer(force=True)
|
i.remove_buffer(force=True)
|
||||||
commonMessageDialogs.blocked_timeline()
|
commonMessageDialogs.blocked_timeline()
|
||||||
@@ -1615,4 +1626,4 @@ class Controller(object):
|
|||||||
|
|
||||||
def save_data_in_db(self):
|
def save_data_in_db(self):
|
||||||
for i in sessions.sessions:
|
for i in sessions.sessions:
|
||||||
sessions.sessions[i].shelve()
|
sessions.sessions[i].save_persistent_data()
|
||||||
|
@@ -109,7 +109,7 @@ class basicTweet(object):
|
|||||||
if results.weightedLength > self.max:
|
if results.weightedLength > self.max:
|
||||||
self.session.sound.play("max_length.ogg")
|
self.session.sound.play("max_length.ogg")
|
||||||
else:
|
else:
|
||||||
self.message.set_title(_(u"%s - %s characters") % (self.title, results.weightedLength))
|
self.message.set_title(_(u"%s - %s characters") % (self.title, len(self.message.get_text())))
|
||||||
|
|
||||||
def spellcheck(self, event=None):
|
def spellcheck(self, event=None):
|
||||||
text = self.message.get_text()
|
text = self.message.get_text()
|
||||||
|
@@ -1,7 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import str
|
|
||||||
from builtins import object
|
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import sound_lib
|
import sound_lib
|
||||||
@@ -151,6 +148,7 @@ class accountSettingsController(globalSettingsController):
|
|||||||
else:
|
else:
|
||||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
|
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", "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.create_reporting()
|
||||||
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_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"])
|
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
|
||||||
@@ -193,6 +191,9 @@ class accountSettingsController(globalSettingsController):
|
|||||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
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"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
|
||||||
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
|
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
|
||||||
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
||||||
if self.dialog.get_value("general", "persist_size") == '':
|
if self.dialog.get_value("general", "persist_size") == '':
|
||||||
self.config["general"]["persist_size"] =-1
|
self.config["general"]["persist_size"] =-1
|
||||||
|
@@ -1,7 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
import output
|
import output
|
||||||
from . import storage
|
from . import storage
|
||||||
from . import wx_menu
|
from . import wx_menu
|
||||||
|
@@ -1,11 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from builtins import object
|
|
||||||
from . import storage
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
from . import wx_manage
|
from . import storage, wx_manage
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
|
|
||||||
class autocompletionManage(object):
|
class autocompletionManage(object):
|
||||||
@@ -32,11 +27,11 @@ class autocompletionManage(object):
|
|||||||
if usr == False:
|
if usr == False:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
data = self.session.twitter.twitter.show_user(screen_name=usr)
|
data = self.session.twitter.twitter.get_user(screen_name=usr)
|
||||||
except:
|
except:
|
||||||
self.dialog.show_invalid_user_error()
|
self.dialog.show_invalid_user_error()
|
||||||
return
|
return
|
||||||
self.database.set_user(data["screen_name"], data["name"], 0)
|
self.database.set_user(data.screen_name, data.name, 0)
|
||||||
self.update_list()
|
self.update_list()
|
||||||
|
|
||||||
def remove_user(self, ev):
|
def remove_user(self, ev):
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from builtins import object
|
|
||||||
from . import storage
|
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
|
import output
|
||||||
from . import wx_settings
|
from . import wx_settings
|
||||||
from . import manage
|
from . import manage
|
||||||
import output
|
from . import storage
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
|
|
||||||
class autocompletionSettings(object):
|
class autocompletionSettings(object):
|
||||||
@@ -30,14 +26,14 @@ class autocompletionSettings(object):
|
|||||||
database = storage.storage(self.buffer.session.session_id)
|
database = storage.storage(self.buffer.session.session_id)
|
||||||
if self.dialog.get("followers_buffer") == True:
|
if self.dialog.get("followers_buffer") == True:
|
||||||
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
|
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
|
||||||
for i in buffer.session.db[buffer.name]["items"]:
|
for i in buffer.session.db[buffer.name]:
|
||||||
database.set_user(i["screen_name"], i["name"], 1)
|
database.set_user(i.screen_name, i.name, 1)
|
||||||
else:
|
else:
|
||||||
database.remove_by_buffer(1)
|
database.remove_by_buffer(1)
|
||||||
if self.dialog.get("friends_buffer") == True:
|
if self.dialog.get("friends_buffer") == True:
|
||||||
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
|
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
|
||||||
for i in buffer.session.db[buffer.name]["items"]:
|
for i in buffer.session.db[buffer.name]:
|
||||||
database.set_user(i["screen_name"], i["name"], 2)
|
database.set_user(i.screen_name, i.name, 2)
|
||||||
else:
|
else:
|
||||||
database.remove_by_buffer(2)
|
database.remove_by_buffer(2)
|
||||||
wx_settings.show_success_dialog()
|
wx_settings.show_success_dialog()
|
||||||
@@ -52,12 +48,12 @@ def execute_at_startup(window, buffer, config):
|
|||||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
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"])
|
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
||||||
for i in buffer.session.db[buffer.name]:
|
for i in buffer.session.db[buffer.name]:
|
||||||
database.set_user(i["screen_name"], i["name"], 1)
|
database.set_user(i.screen_name, i.name, 1)
|
||||||
else:
|
else:
|
||||||
database.remove_by_buffer(1)
|
database.remove_by_buffer(1)
|
||||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
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"])
|
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
||||||
for i in buffer.session.db[buffer.name]:
|
for i in buffer.session.db[buffer.name]:
|
||||||
database.set_user(i["screen_name"], i["name"], 2)
|
database.set_user(i.screen_name, i.name, 2)
|
||||||
else:
|
else:
|
||||||
database.remove_by_buffer(2)
|
database.remove_by_buffer(2)
|
@@ -1,6 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
import os, sqlite3, paths
|
import os, sqlite3, paths
|
||||||
|
|
||||||
class storage(object):
|
class storage(object):
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
import wx
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
from multiplatform_widgets import widgets
|
from multiplatform_widgets import widgets
|
||||||
import application
|
import application
|
||||||
|
|
||||||
class autocompletionManageDialog(widgetUtils.BaseDialog):
|
class autocompletionManageDialog(widgetUtils.BaseDialog):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
|
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
class menu(wx.Menu):
|
class menu(wx.Menu):
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
import wx
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import application
|
import application
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
from arrow import locales
|
from arrow import locales
|
||||||
from arrow.locales import Locale
|
from arrow.locales import Locale
|
||||||
|
|
||||||
@@ -8,88 +7,43 @@ def fix():
|
|||||||
locales.get_locale = get_locale
|
locales.get_locale = get_locale
|
||||||
|
|
||||||
def get_locale(name):
|
def get_locale(name):
|
||||||
locale_cls = locales._locales.get(name.lower())
|
locale_cls = locales._locale_map.get(name.lower())
|
||||||
if locale_cls is None:
|
if locale_cls is None:
|
||||||
name = name[:2]
|
name = name[:2]
|
||||||
locale_cls = locales._locales.get(name.lower())
|
locale_cls = locales._locale_map.get(name.lower())
|
||||||
if locale_cls == None:
|
if locale_cls == None:
|
||||||
return locales.EnglishLocale()
|
return locales.EnglishLocale()
|
||||||
return locale_cls()
|
return locale_cls()
|
||||||
|
|
||||||
class CatalaLocale(Locale):
|
class GalicianLocale(object):
|
||||||
names = ['ca', 'ca_es', 'ca_ca']
|
|
||||||
past = 'Fa {0}'
|
|
||||||
future = '{0}' # I don't know what's the right phrase in catala for the future.
|
|
||||||
|
|
||||||
timeframes = {
|
|
||||||
'now': 'Ara mateix',
|
|
||||||
'seconds': 'segons',
|
|
||||||
'minute': '1 minut',
|
|
||||||
'minutes': '{0} minuts',
|
|
||||||
'hour': 'una hora',
|
|
||||||
'hours': '{0} hores',
|
|
||||||
'day': 'un dia',
|
|
||||||
'days': '{0} dies',
|
|
||||||
'month': 'un mes',
|
|
||||||
'months': '{0} messos',
|
|
||||||
'year': 'un any',
|
|
||||||
'years': '{0} anys',
|
|
||||||
}
|
|
||||||
|
|
||||||
month_names = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
|
|
||||||
month_abbreviations = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
|
|
||||||
day_names = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
|
|
||||||
day_abbreviations = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
|
|
||||||
|
|
||||||
class GalicianLocale(Locale):
|
|
||||||
names = ['gl', 'gl_es', 'gl_gl']
|
names = ['gl', 'gl_es', 'gl_gl']
|
||||||
past = 'Fai {0}'
|
past = 'Hai {0}'
|
||||||
future = 'En {0}'
|
future = 'En {0}'
|
||||||
|
and_word = "e"
|
||||||
|
|
||||||
timeframes = {
|
timeframes = {
|
||||||
'now': 'Agora mesmo',
|
'now': 'Agora',
|
||||||
'seconds': 'segundos',
|
"second": "un segundo",
|
||||||
|
'seconds': '{0} segundos',
|
||||||
'minute': 'un minuto',
|
'minute': 'un minuto',
|
||||||
'minutes': '{0} minutos',
|
'minutes': '{0} minutos',
|
||||||
'hour': 'una hora',
|
'hour': 'unha hora',
|
||||||
'hours': '{0} horas',
|
'hours': '{0} horas',
|
||||||
'day': 'un día',
|
'day': 'un día',
|
||||||
'days': '{0} días',
|
'days': '{0} días',
|
||||||
|
"week": "unha semana",
|
||||||
|
"weeks": "{0} semanas",
|
||||||
'month': 'un mes',
|
'month': 'un mes',
|
||||||
'months': '{0} meses',
|
'months': '{0} meses',
|
||||||
'year': 'un ano',
|
'year': 'un ano',
|
||||||
'years': '{0} anos',
|
'years': '{0} anos',
|
||||||
}
|
}
|
||||||
|
|
||||||
month_names = ['', 'Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro']
|
meridians = {"am": "am", "pm": "pm", "AM": "AM", "PM": "PM"}
|
||||||
month_abbreviations = ['', 'Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xun', 'Xul', 'Ago', 'Set', 'Out', 'Nov', 'Dec']
|
|
||||||
day_names = ['', 'Luns', 'Martes', 'Mércores', 'Xoves', 'Venres', 'Sábado', 'Domingo']
|
|
||||||
day_abbreviations = ['', 'Lun', 'Mar', 'Mer', 'xov', 'Ven' 'Sab', 'Dom']
|
|
||||||
|
|
||||||
class BasqueLocale(Locale):
|
month_names = ['', 'xaneiro', 'febreiro', 'marzo', 'abril', 'maio', 'xuño', 'xullo', 'agosto', 'setembro', 'outubro', 'novembro', 'decembro']
|
||||||
names = ['eu', 'eu_es', 'eu_eu']
|
month_abbreviations = ['', 'xan', 'feb', 'mar', 'abr', 'mai', 'xun', 'xul', 'ago', 'set', 'out', 'nov', 'dec']
|
||||||
past = 'duela {0}'
|
day_names = ['', 'luns', 'martes', 'mércores', 'xoves', 'venres', 'sábado', 'domingo']
|
||||||
future = '{0} igarota'
|
day_abbreviations = ['', 'lun', 'mar', 'mer', 'xov', 'ven', 'sab', 'dom']
|
||||||
|
ordinal_day_re = r"((?P<value>[1-3]?[0-9](?=[ºª]))[ºª])"
|
||||||
timeframes = {
|
|
||||||
'now': 'Orain',
|
|
||||||
# 'second': 'segundu bat',
|
|
||||||
'seconds': 'segundu batzuk', # without specifying a number.
|
|
||||||
#'seconds': '{0} segundu', # specifying a number
|
|
||||||
'minute': 'minutu bat',
|
|
||||||
'minutes': '{0} minutu',
|
|
||||||
'hour': 'ordu bat',
|
|
||||||
'hours': '{0} ordu',
|
|
||||||
'day': 'egun bat',
|
|
||||||
'days': '{0} egun',
|
|
||||||
'month': 'hilabete bat',
|
|
||||||
'months': '{0} hilabete',
|
|
||||||
'year': 'urte bat',
|
|
||||||
'years': '{0} urte',
|
|
||||||
}
|
|
||||||
|
|
||||||
month_names = ['', 'Urtarrilak', 'Otsailak', 'Martxoak', 'Apirilak', 'Maiatzak', 'Ekainak', 'Uztailak', 'Abuztuak', 'Irailak', 'Urriak', 'Azaroak', 'Abenduak']
|
|
||||||
month_abbreviations = ['', 'urt', 'ots', 'mar', 'api', 'mai', 'eka', 'uzt', 'abu', 'ira', 'urr', 'aza', 'abe']
|
|
||||||
day_names = ['', 'Asteleehna', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata', 'Igandea']
|
|
||||||
day_abbreviations = ['', 'al', 'ar', 'az', 'og', 'ol', 'lr', 'ig']
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -2,14 +2,14 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: TW Blue 0.94\n"
|
"Project-Id-Version: TW Blue 0.94\n"
|
||||||
"POT-Creation-Date: 2019-03-17 13:34+Hora estándar romance\n"
|
"POT-Creation-Date: 2019-03-17 13:34+Hora estándar romance\n"
|
||||||
"PO-Revision-Date: 2019-07-22 16:16+0200\n"
|
"PO-Revision-Date: 2020-06-04 21:19+0200\n"
|
||||||
"Last-Translator: Corentin BACQUÉ-CAZENAVE <corentin@progaccess33.net>\n"
|
"Last-Translator: Corentin BACQUÉ-CAZENAVE <corentin@progaccess33.net>\n"
|
||||||
"Language-Team: Corentin BACQUÉ-CAZENAVE <corentin@progaccess.net>\n"
|
"Language-Team: Oreonan <corentin@progaccess.net>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 2.2.3\n"
|
"X-Generator: Poedit 2.3.1\n"
|
||||||
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
|
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
|
||||||
"X-Poedit-Basepath: .\n"
|
"X-Poedit-Basepath: .\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@@ -262,7 +262,7 @@ msgstr "Listes"
|
|||||||
#: ../src\controller\mainController.py:378
|
#: ../src\controller\mainController.py:378
|
||||||
#: ../src\controller\mainController.py:1399
|
#: ../src\controller\mainController.py:1399
|
||||||
msgid "List for {}"
|
msgid "List for {}"
|
||||||
msgstr "Liste de {}"
|
msgstr "Liste {}"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:381
|
#: ../src\controller\mainController.py:381
|
||||||
msgid "Searches"
|
msgid "Searches"
|
||||||
@@ -1988,7 +1988,7 @@ msgstr "Téléchargement de la nouvelle version en cours..."
|
|||||||
|
|
||||||
#: ../src\update\wxUpdater.py:28
|
#: ../src\update\wxUpdater.py:28
|
||||||
msgid "Updating... %s of %s"
|
msgid "Updating... %s of %s"
|
||||||
msgstr "Mise à jour en cours... %s de %s"
|
msgstr "Mise à jour en cours... %s sur %s"
|
||||||
|
|
||||||
#: ../src\update\wxUpdater.py:31
|
#: ../src\update\wxUpdater.py:31
|
||||||
msgid "Done!"
|
msgid "Done!"
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -5,16 +5,16 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: TwBlue 0.80\n"
|
"Project-Id-Version: TwBlue 0.80\n"
|
||||||
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
|
"POT-Creation-Date: 2019-03-17 13:34+Hora estándar romance\n"
|
||||||
"PO-Revision-Date: 2018-08-07 13:46-0500\n"
|
"PO-Revision-Date: 2021-06-25 18:32+0100\n"
|
||||||
"Last-Translator: Manuel Cortez <manuel@manuelcortez.net>\n"
|
"Last-Translator: Nikola Jović <wwenikola123@gmail.com>\n"
|
||||||
"Language-Team: Aleksandar Đurić <agasoft@gmail.com>\n"
|
"Language-Team: Aleksandar Đurić <agasoft@gmail.com>\n"
|
||||||
"Language: sr_RS@latin\n"
|
"Language: sr_RS@latin\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: pygettext.py 1.5\n"
|
"Generated-By: pygettext.py 1.5\n"
|
||||||
"X-Generator: Poedit 2.0.1\n"
|
"X-Generator: Poedit 1.6.10\n"
|
||||||
"X-Poedit-Bookmarks: -1,442,-1,-1,-1,-1,-1,-1,-1,-1\n"
|
"X-Poedit-Bookmarks: -1,442,-1,-1,-1,-1,-1,-1,-1,-1\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
@@ -82,26 +82,24 @@ msgid "Muted users"
|
|||||||
msgstr "Utišani korisnici"
|
msgstr "Utišani korisnici"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:75
|
#: ../src\controller\buffers\twitterBuffers.py:75
|
||||||
#, fuzzy
|
|
||||||
msgid "{username}'s timeline"
|
msgid "{username}'s timeline"
|
||||||
msgstr "Otvori korisničku vremensku crtu"
|
msgstr "Vremenska linija korisnika {username}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:77
|
#: ../src\controller\buffers\twitterBuffers.py:77
|
||||||
msgid "{username}'s likes"
|
msgid "{username}'s likes"
|
||||||
msgstr ""
|
msgstr "Sviđanja korisnika {username}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:79
|
#: ../src\controller\buffers\twitterBuffers.py:79
|
||||||
msgid "{username}'s followers"
|
msgid "{username}'s followers"
|
||||||
msgstr ""
|
msgstr "Pratioci korisnika {username}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:81
|
#: ../src\controller\buffers\twitterBuffers.py:81
|
||||||
msgid "{username}'s friends"
|
msgid "{username}'s friends"
|
||||||
msgstr ""
|
msgstr "Prijatelji korisnika {username}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:83
|
#: ../src\controller\buffers\twitterBuffers.py:83
|
||||||
#, fuzzy
|
|
||||||
msgid "Unknown buffer"
|
msgid "Unknown buffer"
|
||||||
msgstr "Nepoznato"
|
msgstr "Nepoznat kanal"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:86
|
#: ../src\controller\buffers\twitterBuffers.py:86
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:1242
|
#: ../src\controller\buffers\twitterBuffers.py:1242
|
||||||
@@ -117,14 +115,12 @@ msgid "Write the tweet here"
|
|||||||
msgstr "Otkucajte tvit ovde:"
|
msgstr "Otkucajte tvit ovde:"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:194
|
#: ../src\controller\buffers\twitterBuffers.py:194
|
||||||
#, fuzzy
|
|
||||||
msgid "New tweet in {0}"
|
msgid "New tweet in {0}"
|
||||||
msgstr "Novi tvit"
|
msgstr "Novi tvit u kanalu {0}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:197
|
#: ../src\controller\buffers\twitterBuffers.py:197
|
||||||
#, fuzzy
|
|
||||||
msgid "{0} new tweets in {1}."
|
msgid "{0} new tweets in {1}."
|
||||||
msgstr "@{0} citira vaš tvit: {1}"
|
msgstr "{0} novih tvitova u kanalu {1}."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:232
|
#: ../src\controller\buffers\twitterBuffers.py:232
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:676
|
#: ../src\controller\buffers\twitterBuffers.py:676
|
||||||
@@ -180,7 +176,7 @@ msgstr "Podaci o korisniku"
|
|||||||
#: ../src\controller\buffers\twitterBuffers.py:634
|
#: ../src\controller\buffers\twitterBuffers.py:634
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:987
|
#: ../src\controller\buffers\twitterBuffers.py:987
|
||||||
msgid "Opening item in web browser..."
|
msgid "Opening item in web browser..."
|
||||||
msgstr ""
|
msgstr "Otvaram stavku u Web pretraživaču..."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:688
|
#: ../src\controller\buffers\twitterBuffers.py:688
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:855
|
#: ../src\controller\buffers\twitterBuffers.py:855
|
||||||
@@ -194,30 +190,28 @@ msgid "Mention"
|
|||||||
msgstr "Spomeni"
|
msgstr "Spomeni"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:728
|
#: ../src\controller\buffers\twitterBuffers.py:728
|
||||||
#, fuzzy
|
|
||||||
msgid "{0} new direct messages."
|
msgid "{0} new direct messages."
|
||||||
msgstr "Nova direktna poruka"
|
msgstr "{0} novih direktnih poruka."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:731
|
#: ../src\controller\buffers\twitterBuffers.py:731
|
||||||
#, fuzzy
|
|
||||||
msgid "This action is not supported in the buffer yet."
|
msgid "This action is not supported in the buffer yet."
|
||||||
msgstr "Ova radnja nije podržana na ovom kanalu"
|
msgstr "Ova radnja još uvek nije podržana na ovom kanalu."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:741
|
#: ../src\controller\buffers\twitterBuffers.py:741
|
||||||
msgid ""
|
msgid ""
|
||||||
"Getting more items cannot be done in this buffer. Use the direct messages "
|
"Getting more items cannot be done in this buffer. Use the direct messages "
|
||||||
"buffer instead."
|
"buffer instead."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Nemoguće preuzeti dodatne stavke za ovaj kanal. Umesto toga, koristite kanal "
|
||||||
|
"direktne poruke."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:983
|
#: ../src\controller\buffers\twitterBuffers.py:983
|
||||||
#, fuzzy
|
|
||||||
msgid "{0} new followers."
|
msgid "{0} new followers."
|
||||||
msgstr "Novi pratilac."
|
msgstr "{0} novih pratilaca."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitterBuffers.py:1266
|
#: ../src\controller\buffers\twitterBuffers.py:1266
|
||||||
#, fuzzy
|
|
||||||
msgid "This action is not supported in the buffer, yet."
|
msgid "This action is not supported in the buffer, yet."
|
||||||
msgstr "Ova radnja nije podržana na ovom kanalu"
|
msgstr "Ova radnja još uvek nije podržana na ovom kanalu"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:273
|
#: ../src\controller\mainController.py:273
|
||||||
msgid "Ready"
|
msgid "Ready"
|
||||||
@@ -308,9 +302,8 @@ msgid "{0} not found."
|
|||||||
msgstr "{0} nije pronađen."
|
msgstr "{0} nije pronađen."
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:482
|
#: ../src\controller\mainController.py:482
|
||||||
#, fuzzy
|
|
||||||
msgid "Filters cannot be applied on this buffer"
|
msgid "Filters cannot be applied on this buffer"
|
||||||
msgstr "Ova radnja nije podržana na ovom kanalu"
|
msgstr "Filteri se ne mogu primeniti na ovaj kanal"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:535
|
#: ../src\controller\mainController.py:535
|
||||||
#: ../src\controller\mainController.py:552
|
#: ../src\controller\mainController.py:552
|
||||||
@@ -319,9 +312,8 @@ msgid "Select the user"
|
|||||||
msgstr "Izaberite korisnika"
|
msgstr "Izaberite korisnika"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:809 ../src\controller\messages.py:236
|
#: ../src\controller\mainController.py:809 ../src\controller\messages.py:236
|
||||||
#, fuzzy
|
|
||||||
msgid "MMM D, YYYY. H:m"
|
msgid "MMM D, YYYY. H:m"
|
||||||
msgstr "dddd, MMMM D, YYYY H:m:s"
|
msgstr "MMM D, YYYY. H:m"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:934
|
#: ../src\controller\mainController.py:934
|
||||||
msgid "Conversation with {0}"
|
msgid "Conversation with {0}"
|
||||||
@@ -379,12 +371,11 @@ msgstr "Ova lista je već otvorena"
|
|||||||
|
|
||||||
#: ../src\controller\mainController.py:1423
|
#: ../src\controller\mainController.py:1423
|
||||||
#: ../src\controller\mainController.py:1439
|
#: ../src\controller\mainController.py:1439
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"An error happened while trying to connect to the server. Please try later."
|
"An error happened while trying to connect to the server. Please try later."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Dogodilo se nešto neočekivano prilikom prijavljivanja greške. Molimo vas da "
|
"Došlo je do greške pri pokušaju povezivanja na server. Molimo pokušajte "
|
||||||
"pokušate kasnije."
|
"kasnije."
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1475
|
#: ../src\controller\mainController.py:1475
|
||||||
msgid "The auto-reading of new tweets is enabled for this buffer"
|
msgid "The auto-reading of new tweets is enabled for this buffer"
|
||||||
@@ -475,9 +466,8 @@ msgid "%s - %s characters"
|
|||||||
msgstr "%s - %s znakova"
|
msgstr "%s - %s znakova"
|
||||||
|
|
||||||
#: ../src\controller\messages.py:262
|
#: ../src\controller\messages.py:262
|
||||||
#, fuzzy
|
|
||||||
msgid "View item"
|
msgid "View item"
|
||||||
msgstr "Vidi liste"
|
msgstr "Prikaži stavku"
|
||||||
|
|
||||||
#: ../src\controller\settings.py:75
|
#: ../src\controller\settings.py:75
|
||||||
msgid "Direct connection"
|
msgid "Direct connection"
|
||||||
@@ -533,7 +523,8 @@ msgstr "Korisnik je suspendovan."
|
|||||||
msgid "Information for %s"
|
msgid "Information for %s"
|
||||||
msgstr "Informacija za %s"
|
msgstr "Informacija za %s"
|
||||||
|
|
||||||
#: ../src\controller\user.py:66 ../src\extra\AudioUploader\audioUploader.py:124
|
#: ../src\controller\user.py:66
|
||||||
|
#: ../src\extra\AudioUploader\audioUploader.py:124
|
||||||
msgid "Discarded"
|
msgid "Discarded"
|
||||||
msgstr "Odbačeno"
|
msgstr "Odbačeno"
|
||||||
|
|
||||||
@@ -658,9 +649,8 @@ msgstr "Snimam zvučni zapis..."
|
|||||||
|
|
||||||
#: ../src\extra\AudioUploader\transfer.py:78
|
#: ../src\extra\AudioUploader\transfer.py:78
|
||||||
#: ../src\extra\AudioUploader\transfer.py:84
|
#: ../src\extra\AudioUploader\transfer.py:84
|
||||||
#, fuzzy
|
|
||||||
msgid "Error in file upload: {0}"
|
msgid "Error in file upload: {0}"
|
||||||
msgstr "Kod greške {0}"
|
msgstr "Greška u otpremanju datoteke: {0}"
|
||||||
|
|
||||||
#: ../src\extra\AudioUploader\utils.py:27 ../src\update\utils.py:27
|
#: ../src\extra\AudioUploader\utils.py:27 ../src\update\utils.py:27
|
||||||
msgid "%d day, "
|
msgid "%d day, "
|
||||||
@@ -871,28 +861,24 @@ msgid "Suggestions"
|
|||||||
msgstr "Predlozi"
|
msgstr "Predlozi"
|
||||||
|
|
||||||
#: ../src\extra\SpellChecker\wx_ui.py:42
|
#: ../src\extra\SpellChecker\wx_ui.py:42
|
||||||
#, fuzzy
|
|
||||||
msgid "&Ignore"
|
msgid "&Ignore"
|
||||||
msgstr "Zamenari."
|
msgstr "&Zanemari"
|
||||||
|
|
||||||
#: ../src\extra\SpellChecker\wx_ui.py:43
|
#: ../src\extra\SpellChecker\wx_ui.py:43
|
||||||
#, fuzzy
|
|
||||||
msgid "I&gnore all"
|
msgid "I&gnore all"
|
||||||
msgstr "Zanemari sve"
|
msgstr "Z&anemai sve"
|
||||||
|
|
||||||
#: ../src\extra\SpellChecker\wx_ui.py:44
|
#: ../src\extra\SpellChecker\wx_ui.py:44
|
||||||
#, fuzzy
|
|
||||||
msgid "&Replace"
|
msgid "&Replace"
|
||||||
msgstr "Zameni"
|
msgstr "&Zameni"
|
||||||
|
|
||||||
#: ../src\extra\SpellChecker\wx_ui.py:45
|
#: ../src\extra\SpellChecker\wx_ui.py:45
|
||||||
#, fuzzy
|
|
||||||
msgid "R&eplace all"
|
msgid "R&eplace all"
|
||||||
msgstr "Zameni sve"
|
msgstr "Zam&eni sve"
|
||||||
|
|
||||||
#: ../src\extra\SpellChecker\wx_ui.py:46
|
#: ../src\extra\SpellChecker\wx_ui.py:46
|
||||||
msgid "&Add to personal dictionary"
|
msgid "&Add to personal dictionary"
|
||||||
msgstr ""
|
msgstr "&Dodaj u lični rečnik"
|
||||||
|
|
||||||
#: ../src\extra\SpellChecker\wx_ui.py:79
|
#: ../src\extra\SpellChecker\wx_ui.py:79
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -1553,9 +1539,8 @@ msgid "Like a tweet"
|
|||||||
msgstr "Označi tvit sa sviđa mi se"
|
msgstr "Označi tvit sa sviđa mi se"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:15
|
#: ../src\keystrokeEditor\constants.py:15
|
||||||
#, fuzzy
|
|
||||||
msgid "Like/unlike a tweet"
|
msgid "Like/unlike a tweet"
|
||||||
msgstr "Označi tvit sa ne sviđa mi se"
|
msgstr "Označi da ti se tvit sviđa / ne sviđa"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:16
|
#: ../src\keystrokeEditor\constants.py:16
|
||||||
msgid "Unlike a tweet"
|
msgid "Unlike a tweet"
|
||||||
@@ -1594,9 +1579,8 @@ msgid "Open URL"
|
|||||||
msgstr "Otvori vezu"
|
msgstr "Otvori vezu"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:25
|
#: ../src\keystrokeEditor\constants.py:25
|
||||||
#, fuzzy
|
|
||||||
msgid "View in Twitter"
|
msgid "View in Twitter"
|
||||||
msgstr "Pretraži ttwitter"
|
msgstr "Prikaži na Twitteru"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:26
|
#: ../src\keystrokeEditor\constants.py:26
|
||||||
msgid "Increase volume by 5%"
|
msgid "Increase volume by 5%"
|
||||||
@@ -1715,9 +1699,8 @@ msgid "Opens the global settings dialogue"
|
|||||||
msgstr "Otvara dijalog globalnih podešavanja"
|
msgstr "Otvara dijalog globalnih podešavanja"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:54
|
#: ../src\keystrokeEditor\constants.py:54
|
||||||
#, fuzzy
|
|
||||||
msgid "Opens the list manager"
|
msgid "Opens the list manager"
|
||||||
msgstr "Upravljanje listama"
|
msgstr "Otvara upravljanje listama"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:55
|
#: ../src\keystrokeEditor\constants.py:55
|
||||||
msgid "Opens the account settings dialogue"
|
msgid "Opens the account settings dialogue"
|
||||||
@@ -1821,6 +1804,10 @@ msgid ""
|
|||||||
"If you're sure that {0} isn't running, try deleting the file at {1}. If "
|
"If you're sure that {0} isn't running, try deleting the file at {1}. If "
|
||||||
"you're unsure of how to do this, contact the {0} developers."
|
"you're unsure of how to do this, contact the {0} developers."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"{0} je već pokrenut. Zatvorite druge pokrenute prozore aplikacije pre "
|
||||||
|
"pokretanja ovog. Ako ste sigurni da {0} nije pokrenut, pokušajte da obrišete "
|
||||||
|
"datoteku koja se nalazi na {1}. Ako niste sigurni kako ovo da uradite, "
|
||||||
|
"kontaktirajte {0} programere."
|
||||||
|
|
||||||
#: ../src\sessionmanager\wxUI.py:8
|
#: ../src\sessionmanager\wxUI.py:8
|
||||||
msgid "Session manager"
|
msgid "Session manager"
|
||||||
@@ -1926,9 +1913,8 @@ msgid "public"
|
|||||||
msgstr "Javno"
|
msgstr "Javno"
|
||||||
|
|
||||||
#: ../src\sessions\twitter\session.py:169
|
#: ../src\sessions\twitter\session.py:169
|
||||||
#, fuzzy
|
|
||||||
msgid "There are no more items to retrieve in this buffer."
|
msgid "There are no more items to retrieve in this buffer."
|
||||||
msgstr "Nema koordinata u ovom tvitu."
|
msgstr "Nema više stavki koje se mogu preuzeti u ovom kanalu."
|
||||||
|
|
||||||
#: ../src\sessions\twitter\session.py:215
|
#: ../src\sessions\twitter\session.py:215
|
||||||
msgid "%s failed. Reason: %s"
|
msgid "%s failed. Reason: %s"
|
||||||
@@ -1951,13 +1937,12 @@ msgid "Error code {0}"
|
|||||||
msgstr "Kod greške {0}"
|
msgstr "Kod greške {0}"
|
||||||
|
|
||||||
#: ../src\sessions\twitter\wxUI.py:6
|
#: ../src\sessions\twitter\wxUI.py:6
|
||||||
#, fuzzy
|
|
||||||
msgid "Authorising account..."
|
msgid "Authorising account..."
|
||||||
msgstr "Autorizovan nalog %d"
|
msgstr "Autorizacija naloga..."
|
||||||
|
|
||||||
#: ../src\sessions\twitter\wxUI.py:9
|
#: ../src\sessions\twitter\wxUI.py:9
|
||||||
msgid "Enter your PIN code here"
|
msgid "Enter your PIN code here"
|
||||||
msgstr ""
|
msgstr "Ovde upišite vaš PIN kod"
|
||||||
|
|
||||||
#: ../src\sound.py:159
|
#: ../src\sound.py:159
|
||||||
msgid "Stopped."
|
msgid "Stopped."
|
||||||
@@ -2266,19 +2251,20 @@ msgstr ""
|
|||||||
"suspendovan sa tvitera."
|
"suspendovan sa tvitera."
|
||||||
|
|
||||||
#: ../src\wxUI\commonMessageDialogs.py:85
|
#: ../src\wxUI\commonMessageDialogs.py:85
|
||||||
#, fuzzy
|
|
||||||
msgid "Do you really want to delete this filter?"
|
msgid "Do you really want to delete this filter?"
|
||||||
msgstr "Želite li zaista da izbrišete ovu listu?"
|
msgstr "Da li zaista želite da izbrišete ovaj filter?"
|
||||||
|
|
||||||
#: ../src\wxUI\commonMessageDialogs.py:88
|
#: ../src\wxUI\commonMessageDialogs.py:88
|
||||||
msgid "This filter already exists. Please use a different title"
|
msgid "This filter already exists. Please use a different title"
|
||||||
msgstr ""
|
msgstr "Ovaj filter već postoji. Molimo koristite drugi naziv"
|
||||||
|
|
||||||
#: ../src\wxUI\commonMessageDialogs.py:94
|
#: ../src\wxUI\commonMessageDialogs.py:94
|
||||||
msgid ""
|
msgid ""
|
||||||
"{0} quit unexpectedly the last time it was run. If the problem persists, "
|
"{0} quit unexpectedly the last time it was run. If the problem persists, "
|
||||||
"please report it to the {0} developers."
|
"please report it to the {0} developers."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"{0} je neočekivano zatvoren pri poslednjem pokretanju. Ako se problem "
|
||||||
|
"nastavi, molimo prijavite ga{0} programerima."
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:9
|
#: ../src\wxUI\dialogs\attach.py:9
|
||||||
msgid "Add an attachment"
|
msgid "Add an attachment"
|
||||||
@@ -2343,11 +2329,11 @@ msgstr "Pitaj pre zatvaranja {0}"
|
|||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:27
|
#: ../src\wxUI\dialogs\configuration.py:27
|
||||||
msgid "Disable Streaming functions"
|
msgid "Disable Streaming functions"
|
||||||
msgstr ""
|
msgstr "Onemogući streaming funkcije"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:30
|
#: ../src\wxUI\dialogs\configuration.py:30
|
||||||
msgid "Buffer update interval, in minutes"
|
msgid "Buffer update interval, in minutes"
|
||||||
msgstr ""
|
msgstr "Interval ažuriranja kanala, u minutima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:36
|
#: ../src\wxUI\dialogs\configuration.py:36
|
||||||
msgid "Play a sound when {0} launches"
|
msgid "Play a sound when {0} launches"
|
||||||
@@ -2378,7 +2364,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:48
|
#: ../src\wxUI\dialogs\configuration.py:48
|
||||||
msgid "Remember state for mention all and long tweet"
|
msgid "Remember state for mention all and long tweet"
|
||||||
msgstr ""
|
msgstr "Pamti stanje opcija spomeni sve i dug tvit"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:51
|
#: ../src\wxUI\dialogs\configuration.py:51
|
||||||
msgid "Keymap"
|
msgid "Keymap"
|
||||||
@@ -2446,11 +2432,11 @@ msgstr ""
|
|||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:134
|
#: ../src\wxUI\dialogs\configuration.py:134
|
||||||
msgid "Enable automatic speech feedback"
|
msgid "Enable automatic speech feedback"
|
||||||
msgstr ""
|
msgstr "Omogući automatske govorne povratne informacije"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:136
|
#: ../src\wxUI\dialogs\configuration.py:136
|
||||||
msgid "Enable automatic Braille feedback"
|
msgid "Enable automatic Braille feedback"
|
||||||
msgstr ""
|
msgstr "Omogući automatske povratne informacije na brajevom redu"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:144
|
#: ../src\wxUI\dialogs\configuration.py:144
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
@@ -2564,7 +2550,7 @@ msgstr "Proksi"
|
|||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:373
|
#: ../src\wxUI\dialogs\configuration.py:373
|
||||||
msgid "Feedback"
|
msgid "Feedback"
|
||||||
msgstr ""
|
msgstr "Povratne informacije"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:377
|
#: ../src\wxUI\dialogs\configuration.py:377
|
||||||
msgid "Buffers"
|
msgid "Buffers"
|
||||||
@@ -2583,81 +2569,74 @@ msgid "Save"
|
|||||||
msgstr "Sačuvaj"
|
msgstr "Sačuvaj"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:15
|
#: ../src\wxUI\dialogs\filterDialogs.py:15
|
||||||
#, fuzzy
|
|
||||||
msgid "Create a filter for this buffer"
|
msgid "Create a filter for this buffer"
|
||||||
msgstr "Stvori kanal trenutnih tema u trendu"
|
msgstr "Napravi filter za ovaj kanal"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:16
|
#: ../src\wxUI\dialogs\filterDialogs.py:16
|
||||||
msgid "Filter title"
|
msgid "Filter title"
|
||||||
msgstr ""
|
msgstr "Naziv filtera"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:25
|
#: ../src\wxUI\dialogs\filterDialogs.py:25
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
||||||
msgid "Filter by word"
|
msgid "Filter by word"
|
||||||
msgstr ""
|
msgstr "Filtriraj na osnovu reči"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:26
|
#: ../src\wxUI\dialogs\filterDialogs.py:26
|
||||||
msgid "Ignore tweets wich contain the following word"
|
msgid "Ignore tweets wich contain the following word"
|
||||||
msgstr ""
|
msgstr "Zanemari tvitove koji sadrže sledeću reč"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:27
|
#: ../src\wxUI\dialogs\filterDialogs.py:27
|
||||||
#, fuzzy
|
|
||||||
msgid "Ignore tweets without the following word"
|
msgid "Ignore tweets without the following word"
|
||||||
msgstr "Zanemari tvitove iz ovog klijenta"
|
msgstr "Zanemari tvitove bez sledeće reči"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:32
|
#: ../src\wxUI\dialogs\filterDialogs.py:32
|
||||||
msgid "word"
|
msgid "word"
|
||||||
msgstr ""
|
msgstr "Reč"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:37
|
#: ../src\wxUI\dialogs\filterDialogs.py:37
|
||||||
#, fuzzy
|
|
||||||
msgid "Allow retweets"
|
msgid "Allow retweets"
|
||||||
msgstr "Prikaži tvit"
|
msgstr "Dozvoli retvitove"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:38
|
#: ../src\wxUI\dialogs\filterDialogs.py:38
|
||||||
msgid "Allow quoted tweets"
|
msgid "Allow quoted tweets"
|
||||||
msgstr ""
|
msgstr "Dozvoli citirane tvitove"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:39
|
#: ../src\wxUI\dialogs\filterDialogs.py:39
|
||||||
#, fuzzy
|
|
||||||
msgid "Allow replies"
|
msgid "Allow replies"
|
||||||
msgstr "Vremenska linija pratilaca"
|
msgstr "Dozvoli odgovore"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:47
|
#: ../src\wxUI\dialogs\filterDialogs.py:47
|
||||||
msgid "Use this term as a regular expression"
|
msgid "Use this term as a regular expression"
|
||||||
msgstr ""
|
msgstr "Koristi ovaj termin kao regulara nizraz"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:49
|
#: ../src\wxUI\dialogs\filterDialogs.py:49
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
||||||
#, fuzzy
|
|
||||||
msgid "Filter by language"
|
msgid "Filter by language"
|
||||||
msgstr "Izvorni jezik"
|
msgstr "Filtriraj na osnovu jezika"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:50
|
#: ../src\wxUI\dialogs\filterDialogs.py:50
|
||||||
msgid "Load tweets in the following languages"
|
msgid "Load tweets in the following languages"
|
||||||
msgstr ""
|
msgstr "Učitaj tvitove na sledećim jezicima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:51
|
#: ../src\wxUI\dialogs\filterDialogs.py:51
|
||||||
msgid "Ignore tweets in the following languages"
|
msgid "Ignore tweets in the following languages"
|
||||||
msgstr ""
|
msgstr "Zanemari tvitove na sledećim jezicima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:52
|
#: ../src\wxUI\dialogs\filterDialogs.py:52
|
||||||
msgid "Don't filter by language"
|
msgid "Don't filter by language"
|
||||||
msgstr ""
|
msgstr "Ne filtriraj na osnovu jezika"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:63
|
#: ../src\wxUI\dialogs\filterDialogs.py:63
|
||||||
#, fuzzy
|
|
||||||
msgid "Supported languages"
|
msgid "Supported languages"
|
||||||
msgstr "Izvorni jezik"
|
msgstr "Podržani jezici"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:68
|
#: ../src\wxUI\dialogs\filterDialogs.py:68
|
||||||
msgid "Add selected language to filter"
|
msgid "Add selected language to filter"
|
||||||
msgstr ""
|
msgstr "Dodaj izabrani jezik u filter"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:72
|
#: ../src\wxUI\dialogs\filterDialogs.py:72
|
||||||
#, fuzzy
|
|
||||||
msgid "Selected languages"
|
msgid "Selected languages"
|
||||||
msgstr "Izvorni jezik"
|
msgstr "Izabrani jezici"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:74
|
#: ../src\wxUI\dialogs\filterDialogs.py:74
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:132 ../src\wxUI\dialogs\lists.py:20
|
#: ../src\wxUI\dialogs\filterDialogs.py:132 ../src\wxUI\dialogs\lists.py:20
|
||||||
@@ -2666,17 +2645,16 @@ msgid "Remove"
|
|||||||
msgstr "Ukloni"
|
msgstr "Ukloni"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:122
|
#: ../src\wxUI\dialogs\filterDialogs.py:122
|
||||||
#, fuzzy
|
|
||||||
msgid "Manage filters"
|
msgid "Manage filters"
|
||||||
msgstr "Upravljaj nalozima"
|
msgstr "Upravljanje filterima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:124
|
#: ../src\wxUI\dialogs\filterDialogs.py:124
|
||||||
msgid "Filters"
|
msgid "Filters"
|
||||||
msgstr ""
|
msgstr "Filteri"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr ""
|
msgstr "Filter"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\find.py:12
|
#: ../src\wxUI\dialogs\find.py:12
|
||||||
msgid "Find in current buffer"
|
msgid "Find in current buffer"
|
||||||
@@ -2844,9 +2822,8 @@ msgid "Source: "
|
|||||||
msgstr "Izvor:"
|
msgstr "Izvor:"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:342 ../src\wxUI\dialogs\message.py:420
|
#: ../src\wxUI\dialogs\message.py:342 ../src\wxUI\dialogs\message.py:420
|
||||||
#, fuzzy
|
|
||||||
msgid "Date: "
|
msgid "Date: "
|
||||||
msgstr "Datum"
|
msgstr "Datum:"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:405
|
#: ../src\wxUI\dialogs\message.py:405
|
||||||
msgid "View"
|
msgid "View"
|
||||||
@@ -2942,9 +2919,8 @@ msgid "Update your profile"
|
|||||||
msgstr "Ažurirajte vaš profil"
|
msgstr "Ažurirajte vaš profil"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\update_profile.py:11
|
#: ../src\wxUI\dialogs\update_profile.py:11
|
||||||
#, fuzzy
|
|
||||||
msgid "&Name (50 characters maximum)"
|
msgid "&Name (50 characters maximum)"
|
||||||
msgstr "Ime, najviše 20 znakova"
|
msgstr "&Ime(najviše 50 znakova)"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\update_profile.py:22
|
#: ../src\wxUI\dialogs\update_profile.py:22
|
||||||
msgid "&Website"
|
msgid "&Website"
|
||||||
@@ -3060,9 +3036,8 @@ msgid "&Open URL"
|
|||||||
msgstr "otvori vezu"
|
msgstr "otvori vezu"
|
||||||
|
|
||||||
#: ../src\wxUI\menus.py:17 ../src\wxUI\menus.py:53 ../src\wxUI\menus.py:86
|
#: ../src\wxUI\menus.py:17 ../src\wxUI\menus.py:53 ../src\wxUI\menus.py:86
|
||||||
#, fuzzy
|
|
||||||
msgid "&Open in Twitter"
|
msgid "&Open in Twitter"
|
||||||
msgstr "Pretraži ttwitter"
|
msgstr "&Otvori na Twitteru"
|
||||||
|
|
||||||
#: ../src\wxUI\menus.py:19 ../src\wxUI\menus.py:37 ../src\wxUI\menus.py:55
|
#: ../src\wxUI\menus.py:19 ../src\wxUI\menus.py:37 ../src\wxUI\menus.py:55
|
||||||
msgid "&Play audio"
|
msgid "&Play audio"
|
||||||
@@ -3215,14 +3190,12 @@ msgid "New &trending topics buffer..."
|
|||||||
msgstr "Novi kanal sa temama u trendu"
|
msgstr "Novi kanal sa temama u trendu"
|
||||||
|
|
||||||
#: ../src\wxUI\view.py:54
|
#: ../src\wxUI\view.py:54
|
||||||
#, fuzzy
|
|
||||||
msgid "Create a &filter"
|
msgid "Create a &filter"
|
||||||
msgstr "Stvori novu listu"
|
msgstr "Napravi &filter"
|
||||||
|
|
||||||
#: ../src\wxUI\view.py:55
|
#: ../src\wxUI\view.py:55
|
||||||
#, fuzzy
|
|
||||||
msgid "&Manage filters"
|
msgid "&Manage filters"
|
||||||
msgstr "Upravljaj nalozima"
|
msgstr "&Upravljanje filterima"
|
||||||
|
|
||||||
#: ../src\wxUI\view.py:56
|
#: ../src\wxUI\view.py:56
|
||||||
msgid "Find a string in the currently focused buffer..."
|
msgid "Find a string in the currently focused buffer..."
|
||||||
@@ -3274,7 +3247,7 @@ msgstr "{0} &website"
|
|||||||
|
|
||||||
#: ../src\wxUI\view.py:77
|
#: ../src\wxUI\view.py:77
|
||||||
msgid "Get soundpacks for TWBlue"
|
msgid "Get soundpacks for TWBlue"
|
||||||
msgstr ""
|
msgstr "Nabavi zvučne pakete za TW Blue"
|
||||||
|
|
||||||
#: ../src\wxUI\view.py:78
|
#: ../src\wxUI\view.py:78
|
||||||
msgid "About &{0}"
|
msgid "About &{0}"
|
||||||
|
18
src/run_tests.py
Normal file
18
src/run_tests.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*- 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,9 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
""" A base class to be derived in possible new sessions for TWBlue and services."""
|
""" A base class to be derived in possible new sessions for TWBlue and services."""
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import str
|
|
||||||
from builtins import object
|
|
||||||
import os
|
import os
|
||||||
import paths
|
import paths
|
||||||
import output
|
import output
|
||||||
@@ -11,9 +7,8 @@ import time
|
|||||||
import sound
|
import sound
|
||||||
import logging
|
import logging
|
||||||
import config_utils
|
import config_utils
|
||||||
import shelve
|
import sqlitedict
|
||||||
import application
|
import application
|
||||||
import os
|
|
||||||
from . import session_exceptions as Exceptions
|
from . import session_exceptions as Exceptions
|
||||||
log = logging.getLogger("sessionmanager.session")
|
log = logging.getLogger("sessionmanager.session")
|
||||||
|
|
||||||
@@ -59,7 +54,7 @@ class baseSession(object):
|
|||||||
log.debug("Creating config file %s" % (file_,))
|
log.debug("Creating config file %s" % (file_,))
|
||||||
self.settings = config_utils.load_config(os.path.join(paths.config_path(), file_), os.path.join(paths.app_path(), "Conf.defaults"))
|
self.settings = config_utils.load_config(os.path.join(paths.config_path(), file_), os.path.join(paths.app_path(), "Conf.defaults"))
|
||||||
self.init_sound()
|
self.init_sound()
|
||||||
self.deshelve()
|
self.load_persistent_data()
|
||||||
|
|
||||||
def init_sound(self):
|
def init_sound(self):
|
||||||
try: self.sound = sound.soundSystem(self.settings["sound"])
|
try: self.sound = sound.soundSystem(self.settings["sound"])
|
||||||
@@ -73,48 +68,88 @@ class baseSession(object):
|
|||||||
def authorise(self):
|
def authorise(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def shelve(self):
|
def get_sized_buffer(self, buffer, size, reversed=False):
|
||||||
"""Shelve the database to allow for persistance."""
|
""" Returns a list with the amount of items specified by size."""
|
||||||
shelfname=os.path.join(paths.config_path(), str(self.session_id), "cache")
|
if isinstance(buffer, list) and size != -1 and len(buffer) > size:
|
||||||
if self.settings["general"]["persist_size"] == 0:
|
log.debug("Requesting {} items from a list of {} items. Reversed mode: {}".format(size, len(buffer), reversed))
|
||||||
if os.path.exists(shelfname+".dat"):
|
if reversed == True:
|
||||||
os.remove(shelfname+".dat")
|
return buffer[:size]
|
||||||
return
|
|
||||||
try:
|
|
||||||
if not os.path.exists(shelfname+".dat"):
|
|
||||||
output.speak("Generating database, this might take a while.",True)
|
|
||||||
shelf=shelve.open(os.path.join(paths.config_path(), shelfname),'c')
|
|
||||||
for key, value in list(self.db.items()):
|
|
||||||
if type(key) != str and type(key) != str:
|
|
||||||
output.speak("Uh oh, while shelving the database, a key of type " + str(type(key)) + " has been found. It will be converted to type str, but this will cause all sorts of problems on deshelve. Please bring this to the attention of the " + application.name + " developers immediately. More information about the error will be written to the error log.",True)
|
|
||||||
log.error("Uh oh, " + str(key) + " is of type " + str(type(key)) + "!")
|
|
||||||
if type(value) == list and self.settings["general"]["persist_size"] != -1 and len(value) > self.settings["general"]["persist_size"]:
|
|
||||||
shelf[key]=value[self.settings["general"]["persist_size"]:]
|
|
||||||
else:
|
else:
|
||||||
shelf[key]=value
|
return buffer[len(buffer)-size:]
|
||||||
shelf.close()
|
else:
|
||||||
except:
|
return buffer
|
||||||
output.speak("An exception occurred while shelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
|
|
||||||
log.exception("Exception while shelving" + shelfname)
|
|
||||||
os.remove(shelfname)
|
|
||||||
|
|
||||||
def deshelve(self):
|
def save_persistent_data(self):
|
||||||
"""Import a shelved database."""
|
""" Save the data to a persistent sqlite backed file. ."""
|
||||||
shelfname=os.path.join(paths.config_path(), str(self.session_id)+"/cache")
|
dbname=os.path.join(paths.config_path(), str(self.session_id), "cache.db")
|
||||||
|
log.debug("Saving storage information...")
|
||||||
|
# persist_size set to 0 means not saving data actually.
|
||||||
if self.settings["general"]["persist_size"] == 0:
|
if self.settings["general"]["persist_size"] == 0:
|
||||||
if os.path.exists(shelfname+".dat"):
|
if os.path.exists(dbname):
|
||||||
os.remove(shelfname+".dat")
|
os.remove(dbname)
|
||||||
return
|
return
|
||||||
|
# Let's check if we need to create a new SqliteDict object (when loading db in memory) or we just need to call to commit in self (if reading from disk).db.
|
||||||
|
# If we read from disk, we cannot modify the buffer size here as we could damage the app's integrity.
|
||||||
|
# We will modify buffer's size (managed by persist_size) upon loading the db into memory in app startup.
|
||||||
|
if self.settings["general"]["load_cache_in_memory"] and isinstance(self.db, dict):
|
||||||
|
log.debug("Opening database to dump memory contents...")
|
||||||
|
db=sqlitedict.SqliteDict(dbname, 'c')
|
||||||
|
for k in self.db.keys():
|
||||||
|
sized_buff = self.get_sized_buffer(self.db[k], self.settings["general"]["persist_size"], self.settings["general"]["reverse_timelines"])
|
||||||
|
db[k] = sized_buff
|
||||||
|
db.commit(blocking=True)
|
||||||
|
db.close()
|
||||||
|
log.debug("Data has been saved in the database.")
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
shelf=shelve.open(os.path.join(paths.config_path(), shelfname),'c')
|
log.debug("Syncing new data to disk...")
|
||||||
for key,value in list(shelf.items()):
|
if hasattr(self.db, "commit"):
|
||||||
self.db[key]=value
|
self.db.commit()
|
||||||
shelf.close()
|
|
||||||
except:
|
except:
|
||||||
output.speak("An exception occurred while deshelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
|
output.speak(_("An exception occurred while saving the {app} database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the {app} developers.").format(app=application.name),True)
|
||||||
log.exception("Exception while deshelving" + shelfname)
|
log.exception("Exception while saving {}".format(dbname))
|
||||||
|
os.remove(dbname)
|
||||||
|
|
||||||
|
def load_persistent_data(self):
|
||||||
|
"""Import data from a database file from user config."""
|
||||||
|
log.debug("Loading storage data...")
|
||||||
|
dbname=os.path.join(paths.config_path(), str(self.session_id), "cache.db")
|
||||||
|
# If persist_size is set to 0, we should remove the db file as we are no longer going to save anything.
|
||||||
|
if self.settings["general"]["persist_size"] == 0:
|
||||||
|
if os.path.exists(dbname):
|
||||||
|
os.remove(dbname)
|
||||||
|
# Let's return from here, as we are not loading anything.
|
||||||
|
return
|
||||||
|
# try to load the db file.
|
||||||
try:
|
try:
|
||||||
os.remove(shelfname)
|
log.debug("Opening database...")
|
||||||
|
db=sqlitedict.SqliteDict(os.path.join(paths.config_path(), dbname), 'c')
|
||||||
|
# If load_cache_in_memory is set to true, we will load the whole database into memory for faster access.
|
||||||
|
# This is going to be faster when retrieving specific objects, at the cost of more memory.
|
||||||
|
# Setting this to False will read the objects from database as they are needed, which might be slower for bigger datasets.
|
||||||
|
if self.settings["general"]["load_cache_in_memory"]:
|
||||||
|
log.debug("Loading database contents into memory...")
|
||||||
|
for k in db.keys():
|
||||||
|
self.db[k] = db[k]
|
||||||
|
db.commit(blocking=True)
|
||||||
|
db.close()
|
||||||
|
log.debug("Contents were loaded successfully.")
|
||||||
|
else:
|
||||||
|
log.debug("Instantiating database from disk.")
|
||||||
|
self.db = db
|
||||||
|
# We must make sure we won't load more than the amount of buffer specified.
|
||||||
|
log.debug("Checking if we will load all content...")
|
||||||
|
for k in self.db.keys():
|
||||||
|
sized_buffer = self.get_sized_buffer(self.db[k], self.settings["general"]["persist_size"], self.settings["general"]["reverse_timelines"])
|
||||||
|
self.db[k] = sized_buffer
|
||||||
|
if self.db.get("cursors") == None:
|
||||||
|
cursors = dict(direct_messages=-1)
|
||||||
|
self.db["cursors"] = cursors
|
||||||
|
except:
|
||||||
|
output.speak(_("An exception occurred while loading the {app} database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the {app} developers.").format(app=application.name), True)
|
||||||
|
log.exception("Exception while loading {}".format(dbname))
|
||||||
|
try:
|
||||||
|
os.remove(dbname)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -1,11 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
from builtins import str
|
|
||||||
from builtins import chr
|
|
||||||
from builtins import range
|
|
||||||
import platform
|
import platform
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
from . import utils
|
from . import utils
|
||||||
@@ -56,29 +49,28 @@ def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=No
|
|||||||
else:
|
else:
|
||||||
text = StripChars(getattr(tweet, value))
|
text = StripChars(getattr(tweet, value))
|
||||||
if show_screen_names:
|
if show_screen_names:
|
||||||
user = tweet.user.screen_name
|
user = session.get_user(tweet.user).screen_name
|
||||||
else:
|
else:
|
||||||
user = tweet.user.name
|
user = session.get_user(tweet.user).name
|
||||||
source = re.sub(r"(?s)<.*?>", "", tweet.source)
|
source = re.sub(r"(?s)<.*?>", "", tweet.source)
|
||||||
if hasattr(tweet, "retweeted_status"):
|
if hasattr(tweet, "retweeted_status"):
|
||||||
if (hasattr(tweet, "message")) == False and tweet.retweeted_status.is_quote_status == False:
|
if hasattr(tweet, "message") == False and hasattr(tweet.retweeted_status, "is_quote_status") == False:
|
||||||
text = "RT @%s: %s" % (tweet.retweeted_status.user.screen_name, text)
|
text = "RT @%s: %s" % (session.get_user(tweet.retweeted_status.user).screen_name, text)
|
||||||
elif tweet.retweeted_status.is_quote_status:
|
elif hasattr(tweet.retweeted_status, "is_quote_status"):
|
||||||
text = "%s" % (text)
|
text = "%s" % (text)
|
||||||
else:
|
else:
|
||||||
text = "RT @%s: %s" % (tweet.retweeted_status.user.screen_name, text)
|
text = "RT @%s: %s" % (session.get_user(tweet.retweeted_status.user).screen_name, text)
|
||||||
if not hasattr(tweet, "message"):
|
if not hasattr(tweet, "message"):
|
||||||
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
if hasattr(tweet, "retweeted_status"):
|
||||||
|
if hasattr(tweet.retweeted_status, "entities"):
|
||||||
text = utils.expand_urls(text, tweet.retweeted_status.entities)
|
text = utils.expand_urls(text, tweet.retweeted_status.entities)
|
||||||
else:
|
else:
|
||||||
|
if hasattr(tweet, "entities"):
|
||||||
text = utils.expand_urls(text, tweet.entities)
|
text = utils.expand_urls(text, tweet.entities)
|
||||||
if config.app['app-settings']['handle_longtweets']: pass
|
if config.app['app-settings']['handle_longtweets']: pass
|
||||||
return [user+", ", text, ts+", ", source]
|
return [user+", ", text, ts+", ", source]
|
||||||
|
|
||||||
def compose_direct_message(item, db, relative_times, show_screen_names=False, session=None):
|
def compose_direct_message(item, db, relative_times, show_screen_names=False, session=None):
|
||||||
# for a while this function will be together with compose_dm.
|
|
||||||
# this one composes direct messages based on events (new API Endpoints).
|
|
||||||
if system == "Windows":
|
if system == "Windows":
|
||||||
# Let's remove the last 3 digits in the timestamp string.
|
# 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.
|
# Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it.
|
||||||
@@ -121,14 +113,14 @@ def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False,
|
|||||||
value = "text"
|
value = "text"
|
||||||
text = StripChars(getattr(quoted_tweet, value))
|
text = StripChars(getattr(quoted_tweet, value))
|
||||||
if show_screen_names:
|
if show_screen_names:
|
||||||
quoting_user = quoted_tweet.user.screen_name
|
quoting_user = session.get_user(quoted_tweet.user).screen_name
|
||||||
else:
|
else:
|
||||||
quoting_user = quoted_tweet.user.name
|
quoting_user = session.get_user(quoted_tweet.user).name
|
||||||
source = quoted_tweet.source
|
source = quoted_tweet.source
|
||||||
if hasattr(quoted_tweet, "retweeted_status"):
|
if hasattr(quoted_tweet, "retweeted_status"):
|
||||||
text = "rt @%s: %s" % (quoted_tweet.retweeted_status.user.screen_name, text)
|
text = "rt @%s: %s" % (session.get_user(quoted_tweet.retweeted_status.user).screen_name, text)
|
||||||
if text[-1] in chars: text=text+"."
|
if text[-1] in chars: text=text+"."
|
||||||
original_user = original_tweet.user.screen_name
|
original_user = session.get_user(original_tweet.user).screen_name
|
||||||
if hasattr(original_tweet, "message"):
|
if hasattr(original_tweet, "message"):
|
||||||
original_text = original_tweet.message
|
original_text = original_tweet.message
|
||||||
elif hasattr(original_tweet, "full_text"):
|
elif hasattr(original_tweet, "full_text"):
|
||||||
@@ -137,6 +129,11 @@ def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False,
|
|||||||
original_text = StripChars(original_tweet.text)
|
original_text = StripChars(original_tweet.text)
|
||||||
quoted_tweet.message = _(u"{0}. Quoted tweet from @{1}: {2}").format( text, original_user, original_text)
|
quoted_tweet.message = _(u"{0}. Quoted tweet from @{1}: {2}").format( text, original_user, original_text)
|
||||||
quoted_tweet = tweets.clear_url(quoted_tweet)
|
quoted_tweet = tweets.clear_url(quoted_tweet)
|
||||||
|
if hasattr(original_tweet, "entities") and original_tweet.entities.get("urls"):
|
||||||
|
if hasattr(quoted_tweet, "entities") == False:
|
||||||
|
quoted_tweet.entities = {}
|
||||||
|
if quoted_tweet.entities.get("urls") == None:
|
||||||
|
quoted_tweet.entities["urls"] = []
|
||||||
quoted_tweet.entities["urls"].extend(original_tweet.entities["urls"])
|
quoted_tweet.entities["urls"].extend(original_tweet.entities["urls"])
|
||||||
return quoted_tweet
|
return quoted_tweet
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@ def is_long(tweet):
|
|||||||
returns True if a quote is detected, False otherwise."""
|
returns True if a quote is detected, False otherwise."""
|
||||||
if hasattr(tweet, "quoted_status_id") and hasattr(tweet, "quoted_status"):
|
if hasattr(tweet, "quoted_status_id") and hasattr(tweet, "quoted_status"):
|
||||||
return tweet.quoted_status_id
|
return tweet.quoted_status_id
|
||||||
elif hasattr(tweet, "retweeted_status") and hasattr(tweet, "quoted_status_id") and hasattr(tweet, "quoted_status"):
|
elif hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "quoted_status_id") and hasattr(tweet.retweeted_status, "quoted_status"):
|
||||||
return tweet.retweeted_status.quoted_status_id
|
return tweet.retweeted_status.quoted_status_id
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
37
src/sessions/twitter/reduce.py
Normal file
37
src/sessions/twitter/reduce.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Strips unneeded tweet information in order to store tweet objects by using less memory. This is especially useful when buffers start to contain more than a certain amount of items. """
|
||||||
|
from tweepy.models import Status
|
||||||
|
|
||||||
|
def reduce_tweet(tweet):
|
||||||
|
""" generates a new Tweet model with the fields we currently need, excluding everything else including null values and empty collections. """
|
||||||
|
allowed_values = ["created_at", "id", "full_text", "text", "message", "in_reply_to_status_id", "in_reply_to_user_id", "is_quote_status", "lang", "source", "coordinates", "quoted_status_id", "extended_entities"]
|
||||||
|
allowed_entities = ["hashtags", "media", "urls", "user_mentions", "polls"]
|
||||||
|
status_dict = {}
|
||||||
|
for key in allowed_values:
|
||||||
|
if tweet._json.get(key):
|
||||||
|
status_dict[key] = tweet._json[key]
|
||||||
|
entities = dict()
|
||||||
|
for key in allowed_entities:
|
||||||
|
if tweet._json["entities"].get(key) and tweet._json["entities"].get(key) != None:
|
||||||
|
entities[key] = tweet._json["entities"][key]
|
||||||
|
status_dict["entities"] = entities
|
||||||
|
# If tweet comes from the cached database, it does not include an API, so we can pass None here as we do not use that reference to tweepy's API.
|
||||||
|
if hasattr(tweet, "_api"):
|
||||||
|
api = tweet._api
|
||||||
|
else:
|
||||||
|
api = None
|
||||||
|
status = Status().parse(api=api, json=status_dict)
|
||||||
|
# Quotes and retweets are different objects. So we parse a new tweet when we have a quoted or retweeted status here.
|
||||||
|
if tweet._json.get("quoted_status"):
|
||||||
|
quoted_tweet = reduce_tweet(tweet.quoted_status)
|
||||||
|
status.quoted_status = quoted_tweet
|
||||||
|
if tweet._json.get("retweeted_status"):
|
||||||
|
retweeted_tweet = reduce_tweet(tweet.retweeted_status)
|
||||||
|
status.retweeted_status = retweeted_tweet
|
||||||
|
# Adds user ID to here so we can reference it later.
|
||||||
|
# Sometimes, the conversations buffer would send an already reduced tweet here so we will need to return it as is.
|
||||||
|
if isinstance(tweet.user, str) == False:
|
||||||
|
status.user = tweet.user.id_str
|
||||||
|
else:
|
||||||
|
return tweet
|
||||||
|
return status
|
@@ -11,11 +11,13 @@ import application
|
|||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
import tweepy
|
import tweepy
|
||||||
from tweepy.error import TweepError
|
from tweepy.error import TweepError
|
||||||
|
from tweepy.models import User as UserModel
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from keys import keyring
|
from keys import keyring
|
||||||
from sessions import base
|
from sessions import base
|
||||||
from sessions.twitter import utils, compose
|
from sessions.twitter import utils, compose
|
||||||
from sessions.twitter.long_tweets import tweets, twishort
|
from sessions.twitter.long_tweets import tweets, twishort
|
||||||
|
from . import reduce
|
||||||
from .wxUI import authorisationDialog
|
from .wxUI import authorisationDialog
|
||||||
|
|
||||||
log = logging.getLogger("sessions.twitterSession")
|
log = logging.getLogger("sessions.twitterSession")
|
||||||
@@ -37,26 +39,27 @@ class Session(base.baseSession):
|
|||||||
self.db[name] = []
|
self.db[name] = []
|
||||||
if ("users" in self.db) == False:
|
if ("users" in self.db) == False:
|
||||||
self.db["users"] = {}
|
self.db["users"] = {}
|
||||||
|
objects = self.db[name]
|
||||||
if ignore_older and len(self.db[name]) > 0:
|
if ignore_older and len(self.db[name]) > 0:
|
||||||
if self.settings["general"]["reverse_timelines"] == False:
|
if self.settings["general"]["reverse_timelines"] == False:
|
||||||
last_id = self.db[name][0].id
|
last_id = self.db[name][0].id
|
||||||
else:
|
else:
|
||||||
last_id = self.db[name][-1].id
|
last_id = self.db[name][-1].id
|
||||||
|
self.add_users_from_results(data)
|
||||||
for i in data:
|
for i in data:
|
||||||
if ignore_older and last_id != None:
|
if ignore_older and last_id != None:
|
||||||
if i.id < last_id:
|
if i.id < last_id:
|
||||||
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id))
|
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id))
|
||||||
continue
|
continue
|
||||||
if utils.find_item(i.id, self.db[name]) == None and utils.is_allowed(i, self.settings, name) == True:
|
if utils.find_item(i.id, self.db[name]) == None and utils.is_allowed(i, self.settings, name) == True:
|
||||||
i = self.check_quoted_status(i)
|
|
||||||
i = self.check_long_tweet(i)
|
|
||||||
if i == False: continue
|
if i == False: continue
|
||||||
if self.settings["general"]["reverse_timelines"] == False: self.db[name].append(i)
|
reduced_object = reduce.reduce_tweet(i)
|
||||||
else: self.db[name].insert(0, i)
|
reduced_object = self.check_quoted_status(reduced_object)
|
||||||
|
reduced_object = self.check_long_tweet(reduced_object)
|
||||||
|
if self.settings["general"]["reverse_timelines"] == False: objects.append(reduced_object)
|
||||||
|
else: objects.insert(0, reduced_object)
|
||||||
num = num+1
|
num = num+1
|
||||||
if hasattr(i, "user"):
|
self.db[name] = objects
|
||||||
if (i.user.id in self.db["users"]) == False:
|
|
||||||
self.db["users"][i.user.id] = i.user
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
def order_people(self, name, data):
|
def order_people(self, name, data):
|
||||||
@@ -67,11 +70,13 @@ class Session(base.baseSession):
|
|||||||
num = 0
|
num = 0
|
||||||
if (name in self.db) == False:
|
if (name in self.db) == False:
|
||||||
self.db[name] = []
|
self.db[name] = []
|
||||||
|
objects = self.db[name]
|
||||||
for i in data:
|
for i in data:
|
||||||
if utils.find_item(i.id, self.db[name]) == None:
|
if utils.find_item(i.id, self.db[name]) == None:
|
||||||
if self.settings["general"]["reverse_timelines"] == False: self.db[name].append(i)
|
if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
|
||||||
else: self.db[name].insert(0, i)
|
else: objects.insert(0, i)
|
||||||
num = num+1
|
num = num+1
|
||||||
|
self.db[name] = objects
|
||||||
return num
|
return num
|
||||||
|
|
||||||
def order_direct_messages(self, data):
|
def order_direct_messages(self, data):
|
||||||
@@ -82,19 +87,28 @@ class Session(base.baseSession):
|
|||||||
sent = 0
|
sent = 0
|
||||||
if ("direct_messages" in self.db) == False:
|
if ("direct_messages" in self.db) == False:
|
||||||
self.db["direct_messages"] = []
|
self.db["direct_messages"] = []
|
||||||
|
if ("sent_direct_messages" in self.db) == False:
|
||||||
|
self.db["sent_direct_messages"] = []
|
||||||
|
objects = self.db["direct_messages"]
|
||||||
|
sent_objects = self.db["sent_direct_messages"]
|
||||||
for i in data:
|
for i in data:
|
||||||
# Twitter returns sender_id as str, which must be converted to int in order to match to our user_id object.
|
# Twitter returns sender_id as str, which must be converted to int in order to match to our user_id object.
|
||||||
if int(i.message_create["sender_id"]) == self.db["user_id"]:
|
if int(i.message_create["sender_id"]) == self.db["user_id"]:
|
||||||
if "sent_direct_messages" in self.db and utils.find_item(i.id, self.db["sent_direct_messages"]) == None:
|
if "sent_direct_messages" in self.db and utils.find_item(i.id, self.db["sent_direct_messages"]) == None:
|
||||||
if self.settings["general"]["reverse_timelines"] == False: self.db["sent_direct_messages"].append(i)
|
if self.settings["general"]["reverse_timelines"] == False: sent_objects.append(i)
|
||||||
else: self.db["sent_direct_messages"].insert(0, i)
|
else: sent_objects.insert(0, i)
|
||||||
sent = sent+1
|
sent = sent+1
|
||||||
else:
|
else:
|
||||||
if utils.find_item(i.id, self.db["direct_messages"]) == None:
|
if utils.find_item(i.id, self.db["direct_messages"]) == None:
|
||||||
if self.settings["general"]["reverse_timelines"] == False: self.db["direct_messages"].append(i)
|
if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
|
||||||
else: self.db["direct_messages"].insert(0, i)
|
else: objects.insert(0, i)
|
||||||
incoming = incoming+1
|
incoming = incoming+1
|
||||||
|
self.db["direct_messages"] = objects
|
||||||
|
|
||||||
|
self.db["sent_direct_messages"] = sent_objects
|
||||||
pub.sendMessage("sent-dms-updated", total=sent, account=self.db["user_name"])
|
pub.sendMessage("sent-dms-updated", total=sent, account=self.db["user_name"])
|
||||||
|
|
||||||
|
|
||||||
return incoming
|
return incoming
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -105,6 +119,12 @@ class Session(base.baseSession):
|
|||||||
self.reconnection_function_active = False
|
self.reconnection_function_active = False
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
self.lists = []
|
self.lists = []
|
||||||
|
# As users are cached for accessing them with not too many twitter calls,
|
||||||
|
# there could be a weird situation where a deleted user who sent direct messages to the current account will not be able to be retrieved at twitter.
|
||||||
|
# So we need to store an "user deleted" object in the cache, but have the ID of the deleted user in a local reference.
|
||||||
|
# This will be especially useful because if the user reactivates their account later, TWblue will try to retrieve such user again at startup.
|
||||||
|
# If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object.
|
||||||
|
self.deleted_users = {}
|
||||||
|
|
||||||
# @_require_configuration
|
# @_require_configuration
|
||||||
def login(self, verify_credentials=True):
|
def login(self, verify_credentials=True):
|
||||||
@@ -160,35 +180,6 @@ class Session(base.baseSession):
|
|||||||
self.verify_authorisation(pincode)
|
self.verify_authorisation(pincode)
|
||||||
self.authorisation_dialog.Destroy()
|
self.authorisation_dialog.Destroy()
|
||||||
|
|
||||||
def get_more_items(self, update_function, users=False, dm=False, name=None, *args, **kwargs):
|
|
||||||
""" Get more items for twitter objects.
|
|
||||||
update_function str: function to call for getting more items. Must be member of self.twitter.
|
|
||||||
users, dm bool: If any of these is set to True, the function will treat items as users or dm (they need different handling).
|
|
||||||
name str: name of the database item to put new element in."""
|
|
||||||
results = []
|
|
||||||
if "cursor" in kwargs and kwargs["cursor"] == 0:
|
|
||||||
output.speak(_(u"There are no more items to retrieve in this buffer."))
|
|
||||||
return
|
|
||||||
data = getattr(self.twitter, update_function)(*args, **kwargs)
|
|
||||||
if users == True:
|
|
||||||
if type(data) == dict and "next_cursor" in data:
|
|
||||||
if "next_cursor" in data: # There are more objects to retrieve.
|
|
||||||
self.db[name]["cursor"] = data["next_cursor"]
|
|
||||||
else: # Set cursor to 0, wich means no more items available.
|
|
||||||
self.db[name]["cursor"] = 0
|
|
||||||
for i in data["users"]: results.append(i)
|
|
||||||
elif type(data) == list:
|
|
||||||
results.extend(data[1:])
|
|
||||||
elif dm == True:
|
|
||||||
if "next_cursor" in data: # There are more objects to retrieve.
|
|
||||||
self.db[name]["cursor"] = data["next_cursor"]
|
|
||||||
else: # Set cursor to 0, wich means no more items available.
|
|
||||||
self.db[name]["cursor"] = 0
|
|
||||||
for i in data["events"]: results.append(i)
|
|
||||||
else:
|
|
||||||
results.extend(data[1:])
|
|
||||||
return results
|
|
||||||
|
|
||||||
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.
|
||||||
If twitter returns an error, it will not call the method anymore.
|
If twitter returns an error, it will not call the method anymore.
|
||||||
@@ -358,10 +349,11 @@ class Session(base.baseSession):
|
|||||||
value = "full_text"
|
value = "full_text"
|
||||||
else:
|
else:
|
||||||
value = "text"
|
value = "text"
|
||||||
|
if hasattr(quoted_tweet, "entities"):
|
||||||
setattr(quoted_tweet, value, utils.expand_urls(getattr(quoted_tweet, value), quoted_tweet.entities))
|
setattr(quoted_tweet, value, utils.expand_urls(getattr(quoted_tweet, value), quoted_tweet.entities))
|
||||||
if hasattr(quoted_tweet, "quoted_status"):
|
if hasattr(quoted_tweet, "is_quote_status") == True and hasattr(quoted_tweet, "quoted_status"):
|
||||||
original_tweet = quoted_tweet.quoted_status
|
original_tweet = quoted_tweet.quoted_status
|
||||||
elif hasattr(quoted_tweet, "retweeted_status") and hasattr(quoted_tweet.retweeted_status, "quoted_status"):
|
elif hasattr(quoted_tweet, "retweeted_status") and hasattr(quoted_tweet.retweeted_status, "is_quote_status") == True and hasattr(quoted_tweet.retweeted_status, "quoted_status"):
|
||||||
original_tweet = quoted_tweet.retweeted_status.quoted_status
|
original_tweet = quoted_tweet.retweeted_status.quoted_status
|
||||||
else:
|
else:
|
||||||
return quoted_tweet
|
return quoted_tweet
|
||||||
@@ -372,13 +364,17 @@ class Session(base.baseSession):
|
|||||||
value = "message"
|
value = "message"
|
||||||
else:
|
else:
|
||||||
value = "text"
|
value = "text"
|
||||||
|
if hasattr(original_tweet, "entities"):
|
||||||
setattr(original_tweet, value, utils.expand_urls(getattr(original_tweet, value), original_tweet.entities))
|
setattr(original_tweet, value, utils.expand_urls(getattr(original_tweet, value), original_tweet.entities))
|
||||||
return compose.compose_quoted_tweet(quoted_tweet, original_tweet)
|
# ToDo: Shall we check whether we should add show_screen_names here?
|
||||||
|
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, session=self)
|
||||||
|
|
||||||
def check_long_tweet(self, tweet):
|
def check_long_tweet(self, tweet):
|
||||||
""" Process a tweet and add extra info if it's a long tweet made with Twyshort.
|
""" Process a tweet and add extra info if it's a long tweet made with Twyshort.
|
||||||
tweet dict: a tweet object.
|
tweet dict: a tweet object.
|
||||||
returns a tweet with a new argument message, or original tweet if it's not a long tweet."""
|
returns a tweet with a new argument message, or original tweet if it's not a long tweet."""
|
||||||
|
long = False
|
||||||
|
if hasattr(tweet, "entities") and tweet.entities.get("urls"):
|
||||||
long = twishort.is_long(tweet)
|
long = twishort.is_long(tweet)
|
||||||
if long != False and config.app["app-settings"]["handle_longtweets"]:
|
if long != False and config.app["app-settings"]["handle_longtweets"]:
|
||||||
message = twishort.get_full_text(long)
|
message = twishort.get_full_text(long)
|
||||||
@@ -386,35 +382,56 @@ class Session(base.baseSession):
|
|||||||
tweet.quoted_status.message = message
|
tweet.quoted_status.message = message
|
||||||
if tweet.quoted_status.message == False: return False
|
if tweet.quoted_status.message == False: return False
|
||||||
tweet.quoted_status.twishort = True
|
tweet.quoted_status.twishort = True
|
||||||
|
if hasattr(tweet.quoted_status, "entities") and tweet.quoted_status.entities.get("user_mentions"):
|
||||||
for i in tweet.quoted_status.entities["user_mentions"]:
|
for i in tweet.quoted_status.entities["user_mentions"]:
|
||||||
if "@%s" % (i["screen_name"]) not in tweet.quoted_status.message and i["screen_name"] != tweet.user.screen_name:
|
if "@%s" % (i["screen_name"]) not in tweet.quoted_status.message and i["screen_name"] != self.get_user(tweet.user).screen_name:
|
||||||
if hasattr(tweet["quoted_status"], "retweeted_status") and tweet.retweeted_status.user.screen_name == i["screen_name"]:
|
if hasattr(tweet.quoted_status, "retweeted_status") and self.get_user(tweet.retweeted_status.user).screen_name == i["screen_name"]:
|
||||||
continue
|
continue
|
||||||
tweet.quoted_status.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
tweet.quoted_status.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
||||||
else:
|
else:
|
||||||
tweet.message = message
|
tweet.message = message
|
||||||
if tweet.message == False: return False
|
if tweet.message == False: return False
|
||||||
tweet.twishort = True
|
tweet.twishort = True
|
||||||
|
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||||
for i in tweet.entities["user_mentions"]:
|
for i in tweet.entities["user_mentions"]:
|
||||||
if "@%s" % (i["screen_name"]) not in tweet.message and i["screen_name"] != tweet.user.screen_name:
|
if "@%s" % (i["screen_name"]) not in tweet.message and i["screen_name"] != self.get_user(tweet.user).screen_name:
|
||||||
if hasattr(tweet, "retweeted_status") and tweet.retweeted_status.user.screen_name == i["screen_name"]:
|
if hasattr(tweet, "retweeted_status") and self.get_user(tweet.retweeted_status.user).screen_name == i["screen_name"]:
|
||||||
continue
|
continue
|
||||||
|
tweet.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
||||||
return tweet
|
return tweet
|
||||||
|
|
||||||
def get_user(self, id):
|
def get_user(self, id):
|
||||||
""" Returns an user object associated with an ID.
|
""" Returns an user object associated with an ID.
|
||||||
id str: User identifier, provided by Twitter.
|
id str: User identifier, provided by Twitter.
|
||||||
returns an user dict."""
|
returns a tweepy user object."""
|
||||||
if ("users" in self.db) == False or (id in self.db["users"]) == False:
|
if hasattr(id, "id_str"):
|
||||||
|
log.error("Called get_user function by passing a full user id as a parameter.")
|
||||||
|
id = id.id_str
|
||||||
|
# Check if the user has been added to the list of deleted users previously.
|
||||||
|
if id in self.deleted_users:
|
||||||
|
log.debug("Returning user {} from the list of deleted users.".format(id))
|
||||||
|
return self.deleted_users[id]
|
||||||
|
if ("users" in self.db) == False or (str(id) in self.db["users"]) == False:
|
||||||
|
log.debug("Requesting user id {} as it is not present in the users database.".format(id))
|
||||||
try:
|
try:
|
||||||
user = self.twitter.get_user(id=id)
|
user = self.twitter.get_user(id=id)
|
||||||
except TweepError as err:
|
except TweepError as err:
|
||||||
user = dict(screen_name="deleted_account", name="Deleted account")
|
user = UserModel(None)
|
||||||
return user
|
user.screen_name = "deleted_user"
|
||||||
self.db["users"][user.id_str] = user
|
user.id = id
|
||||||
|
user.name = _("Deleted account")
|
||||||
|
if hasattr(err, "api_code") and err.api_code == 50:
|
||||||
|
self.deleted_users[id] = user
|
||||||
return user
|
return user
|
||||||
else:
|
else:
|
||||||
return self.db["users"][id]
|
log.exception("Error when attempting to retrieve an user from Twitter.")
|
||||||
|
return user
|
||||||
|
users = self.db["users"]
|
||||||
|
users[user.id_str] = user
|
||||||
|
self.db["users"] = users
|
||||||
|
return user
|
||||||
|
else:
|
||||||
|
return self.db["users"][str(id)]
|
||||||
|
|
||||||
def get_user_by_screen_name(self, screen_name):
|
def get_user_by_screen_name(self, screen_name):
|
||||||
""" Returns an user identifier associated with a screen_name.
|
""" Returns an user identifier associated with a screen_name.
|
||||||
@@ -422,28 +439,65 @@ class Session(base.baseSession):
|
|||||||
returns an user ID."""
|
returns an user ID."""
|
||||||
if ("users" in self.db) == False:
|
if ("users" in self.db) == False:
|
||||||
user = utils.if_user_exists(self.twitter, screen_name)
|
user = utils.if_user_exists(self.twitter, screen_name)
|
||||||
self.db["users"][user["id_str"]] = user
|
users = self.db["users"]
|
||||||
return user["id_str"]
|
users[user["id"]] = user
|
||||||
|
self.db["users"] = users
|
||||||
|
return user["id"]
|
||||||
else:
|
else:
|
||||||
for i in list(self.db["users"].keys()):
|
for i in list(self.db["users"].keys()):
|
||||||
if self.db["users"][i].screen_name == screen_name:
|
if self.db["users"][i].screen_name == screen_name:
|
||||||
return self.db["users"][i].id_str
|
return self.db["users"][i].id
|
||||||
user = utils.if_user_exists(self.twitter, screen_name)
|
user = utils.if_user_exists(self.twitter, screen_name)
|
||||||
self.db["users"][user.id_str] = user
|
users = self.db["users"]
|
||||||
return user.id_str
|
users[user.id] = user
|
||||||
|
self.db["users"] = users
|
||||||
|
return user.id
|
||||||
|
|
||||||
def save_users(self, user_ids):
|
def save_users(self, user_ids):
|
||||||
""" Adds all new users to the users database. """
|
""" Adds all new users to the users database. """
|
||||||
if len(user_ids) == 0:
|
if len(user_ids) == 0:
|
||||||
return
|
return
|
||||||
log.debug("Received %d user IDS to be added in the database." % (len(user_ids)))
|
log.debug("Received %d user IDS to be added in the database." % (len(user_ids)))
|
||||||
users_to_retrieve = [user_id for user_id in user_ids if user_id not in self.db["users"]]
|
users_to_retrieve = [user_id for user_id in user_ids if (user_id not in self.db["users"] and user_id not in self.deleted_users)]
|
||||||
# Remove duplicates
|
# Remove duplicates
|
||||||
users_to_retrieve = list(dict.fromkeys(users_to_retrieve))
|
users_to_retrieve = list(dict.fromkeys(users_to_retrieve))
|
||||||
if len(users_to_retrieve) == 0:
|
if len(users_to_retrieve) == 0:
|
||||||
return
|
return
|
||||||
log.debug("TWBlue will get %d new users from Twitter." % (len(users_to_retrieve)))
|
log.debug("TWBlue will get %d new users from Twitter." % (len(users_to_retrieve)))
|
||||||
|
try:
|
||||||
users = self.twitter.lookup_users(user_ids=users_to_retrieve, tweet_mode="extended")
|
users = self.twitter.lookup_users(user_ids=users_to_retrieve, tweet_mode="extended")
|
||||||
|
users_db = self.db["users"]
|
||||||
for user in users:
|
for user in users:
|
||||||
self.db["users"][user.id_str] = user
|
users_db[user.id_str] = user
|
||||||
log.debug("Added %d new users" % (len(users)))
|
log.debug("Added %d new users" % (len(users)))
|
||||||
|
self.db["users"] = users_db
|
||||||
|
except TweepError as err:
|
||||||
|
if hasattr(err, "api_code") and err.api_code == 17: # Users not found.
|
||||||
|
log.error("The specified users {} were not found in twitter.".format(user_ids))
|
||||||
|
# Creates a deleted user object for every user_id not found here.
|
||||||
|
# This will make TWBlue to not waste Twitter API calls when attempting to retrieve those users again.
|
||||||
|
# As deleted_users is not saved across restarts, when restarting TWBlue, it will retrieve the correct users if they enabled their accounts.
|
||||||
|
for id in users_to_retrieve:
|
||||||
|
user = UserModel(None)
|
||||||
|
user.screen_name = "deleted_user"
|
||||||
|
user.id = id
|
||||||
|
user.name = _("Deleted account")
|
||||||
|
self.deleted_users[id] = user
|
||||||
|
else:
|
||||||
|
log.exception("An exception happened while attempting to retrieve a list of users from direct messages in Twitter.")
|
||||||
|
|
||||||
|
def add_users_from_results(self, data):
|
||||||
|
users = self.db["users"]
|
||||||
|
for i in data:
|
||||||
|
if hasattr(i, "user"):
|
||||||
|
if isinstance(i.user, str):
|
||||||
|
log.warning("A String was passed to be added as an user. This is normal only if TWBlue tried to load a conversation.")
|
||||||
|
continue
|
||||||
|
if (i.user.id_str in self.db["users"]) == False:
|
||||||
|
users[i.user.id_str] = i.user
|
||||||
|
if hasattr(i, "quoted_status") and (i.quoted_status.user.id_str in self.db["users"]) == False:
|
||||||
|
users[i.quoted_status.user.id_str] = i.quoted_status.user
|
||||||
|
|
||||||
|
if hasattr(i, "retweeted_status") and (i.retweeted_status.user.id_str in self.db["users"]) == False:
|
||||||
|
users[i.retweeted_status.user.id_str] = i.retweeted_status.user
|
||||||
|
self.db["users"] = users
|
||||||
|
@@ -21,28 +21,29 @@ bad_chars = '\'\\\n.,[](){}:;"'
|
|||||||
def find_urls_in_text(text):
|
def find_urls_in_text(text):
|
||||||
return url_re2.findall(text)
|
return url_re2.findall(text)
|
||||||
|
|
||||||
def find_urls (tweet):
|
def find_urls (tweet, twitter_media=False):
|
||||||
urls = []
|
urls = []
|
||||||
|
if twitter_media and hasattr(tweet, "extended_entities"):
|
||||||
|
for mediaItem in tweet.extended_entities["media"]:
|
||||||
|
if mediaItem["type"] == "video":
|
||||||
|
for variant in mediaItem["video_info"]["variants"]:
|
||||||
|
if variant["content_type"] == "video/mp4":
|
||||||
|
urls.append(variant["url"])
|
||||||
|
break
|
||||||
# Let's add URLS from tweet entities.
|
# Let's add URLS from tweet entities.
|
||||||
if hasattr(tweet, "message_create"):
|
if hasattr(tweet, "message_create"):
|
||||||
entities = tweet.message_create["message_data"]["entities"]
|
entities = tweet.message_create["message_data"]["entities"]
|
||||||
else:
|
else:
|
||||||
|
if hasattr(tweet, "entities") == True:
|
||||||
entities = tweet.entities
|
entities = tweet.entities
|
||||||
|
if entities.get("urls") != None:
|
||||||
for i in entities["urls"]:
|
for i in entities["urls"]:
|
||||||
if i["expanded_url"] not in urls:
|
if i["expanded_url"] not in urls:
|
||||||
urls.append(i["expanded_url"])
|
urls.append(i["expanded_url"])
|
||||||
if hasattr(tweet, "quoted_status"):
|
if hasattr(tweet, "quoted_status"):
|
||||||
for i in tweet.quoted_status.entities["urls"]:
|
urls.extend(find_urls(tweet.quoted_status))
|
||||||
if i["expanded_url"] not in urls:
|
|
||||||
urls.append(i["expanded_url"])
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
if hasattr(tweet, "retweeted_status"):
|
||||||
for i in tweet.retweeted_status.entities["urls"]:
|
urls.extend(find_urls(tweet.retweeted_status))
|
||||||
if i["expanded_url"] not in urls:
|
|
||||||
urls.append(i["expanded_url"])
|
|
||||||
if hasattr(tweet["retweeted_status"], "quoted_status"):
|
|
||||||
for i in tweet.retweeted_status.quoted_status.entities["urls"]:
|
|
||||||
if i["expanded_url"] not in urls:
|
|
||||||
urls.append(i["expanded_url"])
|
|
||||||
if hasattr(tweet, "message"):
|
if hasattr(tweet, "message"):
|
||||||
i = "message"
|
i = "message"
|
||||||
elif hasattr(tweet, "full_text"):
|
elif hasattr(tweet, "full_text"):
|
||||||
@@ -69,19 +70,25 @@ def find_list(name, lists):
|
|||||||
if lists[i].name == name: return lists[i].id
|
if lists[i].name == name: return lists[i].id
|
||||||
|
|
||||||
def is_audio(tweet):
|
def is_audio(tweet):
|
||||||
|
# Checks firstly for Twitter videos and audios.
|
||||||
|
if hasattr(tweet, "extended_entities"):
|
||||||
|
for mediaItem in tweet.extended_entities["media"]:
|
||||||
|
if mediaItem["type"] == "video":
|
||||||
|
return True
|
||||||
try:
|
try:
|
||||||
if len(find_urls(tweet)) < 1:
|
if len(find_urls(tweet)) < 1:
|
||||||
return False
|
return False
|
||||||
if hasattr(tweet, "message_create"):
|
if hasattr(tweet, "message_create"):
|
||||||
entities = tweet.message_create["message_data"]["entities"]
|
entities = tweet.message_create["message_data"]["entities"]
|
||||||
else:
|
else:
|
||||||
|
if hasattr(tweet, "entities") == False or tweet.entities.get("hashtags") == None:
|
||||||
|
return False
|
||||||
entities = tweet.entities
|
entities = tweet.entities
|
||||||
if len(entities["hashtags"]) > 0:
|
if len(entities["hashtags"]) > 0:
|
||||||
for i in entities["hashtags"]:
|
for i in entities["hashtags"]:
|
||||||
if i["text"] == "audio":
|
if i["text"] == "audio":
|
||||||
return True
|
return True
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print(tweet.entities["hashtags"])
|
|
||||||
log.exception("Exception while executing is_audio hashtag algorithm")
|
log.exception("Exception while executing is_audio hashtag algorithm")
|
||||||
|
|
||||||
def is_geocoded(tweet):
|
def is_geocoded(tweet):
|
||||||
@@ -92,6 +99,8 @@ def is_media(tweet):
|
|||||||
if hasattr(tweet, "message_create"):
|
if hasattr(tweet, "message_create"):
|
||||||
entities = tweet.message_create["message_data"]["entities"]
|
entities = tweet.message_create["message_data"]["entities"]
|
||||||
else:
|
else:
|
||||||
|
if hasattr(tweet, "entities") == False or tweet.entities.get("hashtags") == None:
|
||||||
|
return False
|
||||||
entities = tweet.entities
|
entities = tweet.entities
|
||||||
if entities.get("media") == None:
|
if entities.get("media") == None:
|
||||||
return False
|
return False
|
||||||
@@ -103,28 +112,29 @@ def is_media(tweet):
|
|||||||
def get_all_mentioned(tweet, conf, field="screen_name"):
|
def get_all_mentioned(tweet, conf, field="screen_name"):
|
||||||
""" Gets all users that have been mentioned."""
|
""" Gets all users that have been mentioned."""
|
||||||
results = []
|
results = []
|
||||||
|
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||||
for i in tweet.entities["user_mentions"]:
|
for i in tweet.entities["user_mentions"]:
|
||||||
if i["screen_name"] != conf["user_name"] and i["screen_name"] != tweet.user.screen_name:
|
if i["screen_name"] != conf["user_name"] and i["id_str"] != tweet.user:
|
||||||
if i.get(field) not in results:
|
if i.get(field) not in results:
|
||||||
results.append(i.get(field))
|
results.append(i.get(field))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_all_users(tweet, conf):
|
def get_all_users(tweet, session):
|
||||||
string = []
|
string = []
|
||||||
|
user = session.get_user(tweet.user)
|
||||||
if hasattr(tweet, "retweeted_status"):
|
if hasattr(tweet, "retweeted_status"):
|
||||||
string.append(tweet.user.screen_name)
|
string.append(user.screen_name)
|
||||||
tweet = tweet.retweeted_status
|
tweet = tweet.retweeted_status
|
||||||
if hasattr(tweet, "sender"):
|
|
||||||
string.append(tweet.sender.screen_name)
|
|
||||||
else:
|
else:
|
||||||
if tweet.user.screen_name != conf["user_name"]:
|
if user.screen_name != session.db["user_name"]:
|
||||||
string.append(tweet.user.screen_name)
|
string.append(user.screen_name)
|
||||||
|
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||||
for i in tweet.entities["user_mentions"]:
|
for i in tweet.entities["user_mentions"]:
|
||||||
if i["screen_name"] != conf["user_name"] and i["screen_name"] != tweet.user.screen_name:
|
if i["screen_name"] != session.db["user_name"] and i["screen_name"] != user.screen_name:
|
||||||
if i["screen_name"] not in string:
|
if i["screen_name"] not in string:
|
||||||
string.append(i["screen_name"])
|
string.append(i["screen_name"])
|
||||||
if len(string) == 0:
|
if len(string) == 0:
|
||||||
string.append(tweet.user.screen_name)
|
string.append(user.screen_name)
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def if_user_exists(twitter, user):
|
def if_user_exists(twitter, user):
|
||||||
@@ -144,7 +154,7 @@ def is_allowed(tweet, settings, buffer_name):
|
|||||||
tweet_data = {}
|
tweet_data = {}
|
||||||
if hasattr(tweet, "retweeted_status"):
|
if hasattr(tweet, "retweeted_status"):
|
||||||
tweet_data["retweet"] = True
|
tweet_data["retweet"] = True
|
||||||
if tweet.in_reply_to_status_id_str != None:
|
if tweet.in_reply_to_status_id != None:
|
||||||
tweet_data["reply"] = True
|
tweet_data["reply"] = True
|
||||||
if hasattr(tweet, "quoted_status"):
|
if hasattr(tweet, "quoted_status"):
|
||||||
tweet_data["quote"] = True
|
tweet_data["quote"] = True
|
||||||
@@ -209,6 +219,8 @@ def twitter_error(error):
|
|||||||
|
|
||||||
def expand_urls(text, entities):
|
def expand_urls(text, entities):
|
||||||
""" Expand all URLS present in text with information found in entities"""
|
""" Expand all URLS present in text with information found in entities"""
|
||||||
|
if entities.get("urls") == None:
|
||||||
|
return text
|
||||||
urls = find_urls_in_text(text)
|
urls = find_urls_in_text(text)
|
||||||
for url in entities["urls"]:
|
for url in entities["urls"]:
|
||||||
if url["url"] in text:
|
if url["url"] in text:
|
||||||
|
1
src/test/__init__.py
Normal file
1
src/test/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
200
src/test/test_cache.py
Normal file
200
src/test/test_cache.py
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# -*- 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,7 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import range
|
|
||||||
import logging as original_logger
|
import logging as original_logger
|
||||||
import wx
|
import wx
|
||||||
import application
|
import application
|
||||||
@@ -127,6 +124,7 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
self.persist_size = wx.TextCtrl(self, -1)
|
self.persist_size = wx.TextCtrl(self, -1)
|
||||||
sizer.Add(PersistSizeLabel, 0, wx.ALL, 5)
|
sizer.Add(PersistSizeLabel, 0, wx.ALL, 5)
|
||||||
sizer.Add(self.persist_size, 0, wx.ALL, 5)
|
sizer.Add(self.persist_size, 0, wx.ALL, 5)
|
||||||
|
self.load_cache_in_memory = wx.CheckBox(self, wx.NewId(), _("Load cache for tweets in memory (much faster in big datasets but requires more RAM)"))
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
class reporting(wx.Panel, baseDialog.BaseWXDialog):
|
class reporting(wx.Panel, baseDialog.BaseWXDialog):
|
||||||
|
@@ -221,7 +221,7 @@ class dm(textLimited):
|
|||||||
|
|
||||||
def __init__(self, title, message, users, *args, **kwargs):
|
def __init__(self, title, message, users, *args, **kwargs):
|
||||||
super(dm, self).__init__()
|
super(dm, self).__init__()
|
||||||
self.createControls(message, title, users)
|
self.createControls(title, message, users)
|
||||||
# self.onTimer(wx.EVT_CHAR_HOOK)
|
# self.onTimer(wx.EVT_CHAR_HOOK)
|
||||||
# self.SetClientSize(self.mainBox.CalcMin())
|
# self.SetClientSize(self.mainBox.CalcMin())
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{"current_version": "3",
|
{"current_version": "6",
|
||||||
"description": "Snapshot version.",
|
"description": "Snapshot version.",
|
||||||
"date": "unknown",
|
"date": "unknown",
|
||||||
"downloads":
|
"downloads":
|
||||||
|
Reference in New Issue
Block a user