mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2024-11-29 22:23:12 -06:00
Merge branch 'next-gen' into mastodon
This commit is contained in:
commit
d0322e7131
@ -39,5 +39,5 @@ florian Ionașcu
|
|||||||
Christian Leo Mameli
|
Christian Leo Mameli
|
||||||
Natalia Hedlund (Наталья Хедлунд)
|
Natalia Hedlund (Наталья Хедлунд)
|
||||||
Valeria (Валерия)
|
Valeria (Валерия)
|
||||||
Oreonan
|
Corentin Bacqué-Cazenave
|
||||||
Artem Plaksin (maniyax)
|
Artem Plaksin (maniyax)
|
||||||
|
@ -1,12 +1,34 @@
|
|||||||
TWBlue Changelog
|
TWBlue Changelog
|
||||||
|
|
||||||
## changes in this version
|
## changes in this version
|
||||||
|
|
||||||
|
* We have added Experimental support for templates in the invisible interface. The GUI will remain unchanged for now:
|
||||||
|
* Each object (tweet, received direct message, sent direct message and people) has its own template in the settings. You can edit those templates from the account settings dialog, in the new "templates" tab.
|
||||||
|
* Every template is composed of the group of variables you want to display for each object. Each variable will start with a dollar sign ($) and cannot contain spaces or special characters. Templates can include arbitrary text that will not be processed. When editing the example templates, you can get an idea of the variables that are available for each object by using the template editing dialog. When you press enter on a variable from the list of available variables, it will be added to the template automatically. When you try to save a template, TWBlue will warn you if the template is incorrectly formatted or if it includes variables that do not exist in the information provided by objects. It is also possible to return to default values from the same dialog when editing a template.
|
||||||
|
* TWBlue can display image descriptions within Tweet templates. For that, you can use the $image_description variable in your template.
|
||||||
|
* We have restored conversation and threads support powered by Twitter API V2 thanks to a set of improvements we have done in the application, as well as more generous limits to Tweet monthly cap by Twitter.
|
||||||
|
* In the Windows 11 Keymap, the default shortcut to open the keystrokes editor is now CTRL+Alt+Windows+K to avoid conflicts with the new global mute microphone shortcut.
|
||||||
|
* TWBlue show display properly HTML entities in tweet's text.
|
||||||
|
* TWBlue should no longer load old tweets in buffers.
|
||||||
|
* Fixed issue when uploading attachments (images, videos or gif files) while sending tweets or replies.
|
||||||
|
* Fixed an error that was making TWBlue to ask for a restart after saving account settings, even if such restart was not required. ([#413,](https://github.com/manuelcortez/TWBlue/issues/413))
|
||||||
|
|
||||||
|
## Changes in version 2021.11.12
|
||||||
|
|
||||||
|
* Now it is possible to create a tweet from a trending topics buffer again.
|
||||||
|
* TWBlue now includes a completely new set of dialogs to handle tweeting, replying and sending direct messages that takes advantage of more Twitter features.
|
||||||
|
* It is possible to add videos in tweets and direct messages by using the new "add" button, located in every dialog where media can be added. Twitter suggests to add videos from 5 seconds up to 2 minutes lenght, in mp4 format (video Codec H.264 and audio codec AAC). Currently, TWBlue does not check if the uploaded video complies with Twitter media requirements. You can add only a video in a tweet or direct message. No other kind of media can be added after a video is in a tweet. If the video was unable to be uploaded successfully, the tweet or direct message won't be created.
|
||||||
|
* Now you can add a poll to tweets. Polls can have up to 4 different options and allow voting up to 7 days after being created. Take into account, though, that currently TWBlue does not support reading polls in tweets.
|
||||||
|
* TWBlue now support threads while creating a new tweet. There is a new button, called add tweet which will add the current tweet to the thread and will allow you to write another tweet in the thread. Every tweet might include media (up to 4 photos, or one GIF image or a video) or up to one poll.
|
||||||
|
* Some functionality was removed from tweet dialogs within TWBlue. Particularly, URL shorteners and long tweets via Twishort. You still can read long tweets posted via Twishort, though.
|
||||||
|
|
||||||
|
## Changes in version 2021.11.07
|
||||||
|
|
||||||
* TWBlue should retrieve tweets from threads and conversations in a more reliable way. Tweets in the same thread (made by the same author) will be sorted correctly, although replies to the thread (made by different people) may not be ordered in the same way they are displayed in Twitter apps. ([#417](https://github.com/manuelcortez/TWBlue/issues/417))
|
* TWBlue should retrieve tweets from threads and conversations in a more reliable way. Tweets in the same thread (made by the same author) will be sorted correctly, although replies to the thread (made by different people) may not be ordered in the same way they are displayed in Twitter apps. ([#417](https://github.com/manuelcortez/TWBlue/issues/417))
|
||||||
* fixed a bug when clearing the direct messages buffer. ([#418](https://github.com/manuelcortez/TWBlue/issues/418))
|
|
||||||
* fixed an issue that was making TWBlue to show incorrectly titles for trending topic buffers upon startup. ([#421](https://github.com/manuelcortez/TWBlue/issues/421))
|
|
||||||
* When creating a filter, TWBlue will show an error if user has not provided a name for the filter. Before, unnamed filters were a cause of config breaks in the application.
|
* When creating a filter, TWBlue will show an error if user has not provided a name for the filter. Before, unnamed filters were a cause of config breaks in the application.
|
||||||
* It is again possible to read the changelog for TWBlue from the help menu in the menu bar.
|
* It is again possible to read the changelog for TWBlue from the help menu in the menu bar.
|
||||||
|
* fixed a bug when clearing the direct messages buffer. ([#418](https://github.com/manuelcortez/TWBlue/issues/418))
|
||||||
|
* fixed an issue that was making TWBlue to show incorrectly titles for trending topic buffers upon startup. ([#421](https://github.com/manuelcortez/TWBlue/issues/421))
|
||||||
|
* fixed an issue that was making users of the graphical user interface to delete a buffer if a trends buffer was opened in the same session.
|
||||||
* Updated Spanish, Japanese and french translations.
|
* Updated Spanish, Japanese and french translations.
|
||||||
|
|
||||||
## Changes in Version 2021.10.30
|
## Changes in Version 2021.10.30
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ SetCompress auto
|
|||||||
SetCompressor /solid lzma
|
SetCompressor /solid lzma
|
||||||
SetDatablockOptimize on
|
SetDatablockOptimize on
|
||||||
VIAddVersionKey ProductName "TWBlue"
|
VIAddVersionKey ProductName "TWBlue"
|
||||||
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
|
VIAddVersionKey LegalCopyright "Copyright 2014-2022 MCV Software."
|
||||||
VIAddVersionKey ProductVersion "0.95.0"
|
VIAddVersionKey ProductVersion "0.95.0"
|
||||||
VIAddVersionKey FileVersion "0.95.0"
|
VIAddVersionKey FileVersion "0.95.0"
|
||||||
VIProductVersion "0.95.0"
|
VIProductVersion "0.95.0"
|
||||||
|
@ -14,7 +14,7 @@ retweet_mode = string(default="ask")
|
|||||||
persist_size = integer(default=0)
|
persist_size = integer(default=0)
|
||||||
load_cache_in_memory=boolean(default=True)
|
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'))
|
||||||
|
|
||||||
[sound]
|
[sound]
|
||||||
volume = float(default=1.0)
|
volume = float(default=1.0)
|
||||||
@ -48,6 +48,12 @@ ocr_language = string(default="")
|
|||||||
braille_reporting = boolean(default=True)
|
braille_reporting = boolean(default=True)
|
||||||
speech_reporting = boolean(default=True)
|
speech_reporting = boolean(default=True)
|
||||||
|
|
||||||
|
[templates]
|
||||||
|
tweet = string(default="$display_name, $text $image_descriptions $date. $source")
|
||||||
|
dm = string(default="$sender_display_name, $text $date")
|
||||||
|
dm_sent = string(default="Dm to $recipient_display_name, $text $date")
|
||||||
|
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.")
|
||||||
|
|
||||||
[filters]
|
[filters]
|
||||||
|
|
||||||
[user-aliases]
|
[user-aliases]
|
@ -1,16 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
|
||||||
|
|
||||||
name = 'TWBlue'
|
name = 'TWBlue'
|
||||||
short_name='twblue'
|
short_name='twblue'
|
||||||
update_url = 'https://twblue.es/updates/updates.php'
|
update_url = 'https://twblue.es/updates/updates.php'
|
||||||
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/updates.json'
|
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/updates.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-2022, MCV Software."
|
||||||
description = name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features."
|
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)", "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)"]
|
translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Corentin Bacqué-Cazenave (Français)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"]
|
||||||
url = u"https://twblue.es"
|
url = "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 = []
|
||||||
version = "11"
|
version = "11"
|
||||||
|
@ -4,6 +4,7 @@ from validate import Validator, ValidateError
|
|||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from wxUI import commonMessageDialogs
|
||||||
log = getLogger("config_utils")
|
log = getLogger("config_utils")
|
||||||
|
|
||||||
class ConfigLoadError(Exception): pass
|
class ConfigLoadError(Exception): pass
|
||||||
@ -21,6 +22,7 @@ def load_config(config_path, configspec_path=None, copy=True, *args, **kwargs):
|
|||||||
return config
|
return config
|
||||||
else:
|
else:
|
||||||
log.exception("Error in config file: {0}".format(validated,))
|
log.exception("Error in config file: {0}".format(validated,))
|
||||||
|
commonMessageDialogs.invalid_configuration()
|
||||||
|
|
||||||
def is_blank(arg):
|
def is_blank(arg):
|
||||||
"Check if a line is blank."
|
"Check if a line is blank."
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
import os
|
|
||||||
import widgetUtils
|
|
||||||
import logging
|
|
||||||
from wxUI.dialogs import attach as gui
|
|
||||||
log = logging.getLogger("controller.attach")
|
|
||||||
|
|
||||||
class attach(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.attachments = list()
|
|
||||||
self.dialog = gui.attachDialog()
|
|
||||||
widgetUtils.connect_event(self.dialog.photo, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
|
||||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
|
|
||||||
self.dialog.get_response()
|
|
||||||
log.debug("Attachments controller started.")
|
|
||||||
|
|
||||||
def upload_image(self, *args, **kwargs):
|
|
||||||
image, description = self.dialog.get_image()
|
|
||||||
if image != None:
|
|
||||||
imageInfo = {"type": "photo", "file": image, "description": description}
|
|
||||||
log.debug("Image data to upload: %r" % (imageInfo,))
|
|
||||||
self.attachments.append(imageInfo)
|
|
||||||
info = [_(u"Photo"), description]
|
|
||||||
self.dialog.attachments.insert_item(False, *info)
|
|
||||||
self.dialog.remove.Enable(True)
|
|
||||||
|
|
||||||
def remove_attachment(self, *args, **kwargs):
|
|
||||||
current_item = self.dialog.attachments.get_selected()
|
|
||||||
log.debug("Removing item %d" % (current_item,))
|
|
||||||
if current_item == -1: current_item = 0
|
|
||||||
self.attachments.pop(current_item)
|
|
||||||
self.dialog.attachments.remove_item(current_item)
|
|
||||||
self.check_remove_status()
|
|
||||||
log.debug("Removed")
|
|
||||||
|
|
||||||
def check_remove_status(self):
|
|
||||||
if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0:
|
|
||||||
self.dialog.remove.Enable(False)
|
|
@ -125,6 +125,9 @@ class Buffer(object):
|
|||||||
def share_item(self):
|
def share_item(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def can_share(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def destroy_status(self):
|
def destroy_status(self):
|
||||||
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.base import base
|
from controller.buffers.base import base
|
||||||
from sessions.twitter import compose, utils, reduce
|
from sessions.twitter import compose, utils, reduce, templates
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from tweepy.cursor import Cursor
|
from tweepy.cursor import Cursor
|
||||||
@ -84,42 +84,15 @@ class BaseBuffer(base.Buffer):
|
|||||||
return _(u"Unknown buffer")
|
return _(u"Unknown buffer")
|
||||||
|
|
||||||
def post_status(self, *args, **kwargs):
|
def post_status(self, *args, **kwargs):
|
||||||
item = None
|
title = _("Tweet")
|
||||||
title = _(u"Tweet")
|
caption = _("Write the tweet here")
|
||||||
caption = _(u"Write the tweet here")
|
|
||||||
tweet = messages.tweet(self.session, title, caption, "")
|
tweet = messages.tweet(self.session, title, caption, "")
|
||||||
if tweet.message.get_response() == widgetUtils.OK:
|
response = tweet.message.ShowModal()
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
if response == wx.ID_OK:
|
||||||
config.app["app-settings"]["longtweet"] = tweet.message.long_tweet.GetValue()
|
tweet_data = tweet.get_tweet_data()
|
||||||
config.app.write()
|
call_threaded(self.session.send_tweet, *tweet_data)
|
||||||
text = tweet.message.get_text()
|
if hasattr(tweet.message, "destroy"):
|
||||||
if len(text) > 280 and tweet.message.get("long_tweet") == True:
|
tweet.message.destroy()
|
||||||
if not hasattr(tweet, "attachments"):
|
|
||||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
|
|
||||||
else:
|
|
||||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
|
|
||||||
if not hasattr(tweet, "attachments") or len(tweet.attachments) == 0:
|
|
||||||
item = self.session.api_call(call_name="update_status", status=text, _sound="tweet_send.ogg", tweet_mode="extended")
|
|
||||||
else:
|
|
||||||
call_threaded(self.post_with_media, text=text, attachments=tweet.attachments)
|
|
||||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
|
||||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
|
||||||
# if item != None:
|
|
||||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
|
||||||
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
|
|
||||||
self.session.settings.write()
|
|
||||||
|
|
||||||
def post_with_media(self, text, attachments):
|
|
||||||
media_ids = []
|
|
||||||
for i in attachments:
|
|
||||||
img = self.session.twitter.media_upload(i["file"])
|
|
||||||
self.session.twitter.create_media_metadata(media_id=img.media_id, alt_text=i["description"])
|
|
||||||
media_ids.append(img.media_id)
|
|
||||||
item = self.session.twitter.update_status(status=text, media_ids=media_ids)
|
|
||||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
|
||||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
|
||||||
# if item != None:
|
|
||||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
|
||||||
|
|
||||||
def get_formatted_message(self):
|
def get_formatted_message(self):
|
||||||
if self.type == "dm" or self.name == "direct_messages":
|
if self.type == "dm" or self.name == "direct_messages":
|
||||||
@ -127,8 +100,10 @@ class BaseBuffer(base.Buffer):
|
|||||||
return self.get_message()
|
return self.get_message()
|
||||||
|
|
||||||
def get_message(self):
|
def get_message(self):
|
||||||
|
template = self.session.settings["templates"]["tweet"]
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
return " ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session))
|
t = templates.render_tweet(tweet, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
def get_full_tweet(self):
|
def get_full_tweet(self):
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
@ -167,7 +142,7 @@ class BaseBuffer(base.Buffer):
|
|||||||
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
|
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
|
||||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||||
if self.name != "direct_messages":
|
if self.name != "direct_messages":
|
||||||
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
|
val = self.session.call_paged(self.function, self.name, *self.args, **self.kwargs)
|
||||||
else:
|
else:
|
||||||
# 50 results are allowed per API call, so let's assume max value can be 50.
|
# 50 results are allowed per API call, so let's assume max value can be 50.
|
||||||
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
|
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
|
||||||
@ -199,9 +174,9 @@ class BaseBuffer(base.Buffer):
|
|||||||
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:
|
||||||
self.username = val[0].user.screen_name
|
self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
|
||||||
elif "-favorite" in self.name:
|
elif "-favorite" in self.name:
|
||||||
self.username = self.session.api_call("get_user", **self.kwargs).screen_name
|
self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
|
||||||
self.finished_timeline = True
|
self.finished_timeline = True
|
||||||
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
||||||
self.session.sound.play(self.sound)
|
self.session.sound.play(self.sound)
|
||||||
@ -416,13 +391,18 @@ class BaseBuffer(base.Buffer):
|
|||||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
||||||
return tweet
|
return tweet
|
||||||
|
|
||||||
|
def can_share(self):
|
||||||
|
tweet = self.get_right_tweet()
|
||||||
|
user = self.session.get_user(tweet.user)
|
||||||
|
is_protected = user.protected
|
||||||
|
return is_protected==False
|
||||||
|
|
||||||
@_tweets_exist
|
@_tweets_exist
|
||||||
def reply(self, *args, **kwargs):
|
def reply(self, *args, **kwargs):
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
user = self.session.get_user(tweet.user)
|
user = self.session.get_user(tweet.user)
|
||||||
screen_name = user.screen_name
|
screen_name = user.screen_name
|
||||||
id = tweet.id
|
id = tweet.id
|
||||||
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")
|
ids = utils.get_all_mentioned(tweet, self.session.db, field="id")
|
||||||
# Build the window title
|
# Build the window title
|
||||||
@ -430,38 +410,14 @@ class BaseBuffer(base.Buffer):
|
|||||||
title=_("Reply to {arg0}").format(arg0=screen_name)
|
title=_("Reply to {arg0}").format(arg0=screen_name)
|
||||||
else:
|
else:
|
||||||
title=_("Reply")
|
title=_("Reply")
|
||||||
message = messages.reply(self.session, title, _(u"Reply to %s") % (screen_name,), "", users=users, ids=ids)
|
message = messages.reply(self.session, title, _("Reply to %s") % (screen_name,), "", users=users, ids=ids)
|
||||||
if message.message.get_response() == widgetUtils.OK:
|
if message.message.ShowModal() == widgetUtils.OK:
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||||
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
|
|
||||||
if len(users) > 0:
|
if len(users) > 0:
|
||||||
config.app["app-settings"]["mention_all"] = message.message.mentionAll.GetValue()
|
config.app["app-settings"]["mention_all"] = message.message.mention_all.GetValue()
|
||||||
config.app.write()
|
config.app.write()
|
||||||
params = {"_sound": "reply_send.ogg", "in_reply_to_status_id": id, "tweet_mode": "extended"}
|
tweet_data = dict(text=message.message.text.GetValue(), attachments=message.attachments, poll_options=message.poll_options, poll_period=message.poll_period)
|
||||||
text = message.message.get_text()
|
call_threaded(self.session.reply, in_reply_to_status_id=id, text=message.message.text.GetValue(), attachments=message.attachments, exclude_reply_user_ids=message.get_ids())
|
||||||
if twishort_enabled == False:
|
|
||||||
excluded_ids = message.get_ids()
|
|
||||||
params["exclude_reply_user_ids"] =excluded_ids
|
|
||||||
params["auto_populate_reply_metadata"] =True
|
|
||||||
else:
|
|
||||||
mentioned_people = message.get_people()
|
|
||||||
text = "@"+screen_name+" "+mentioned_people+u" "+text
|
|
||||||
if len(text) > 280 and message.message.get("long_tweet") == True:
|
|
||||||
if message.image == None:
|
|
||||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
|
|
||||||
else:
|
|
||||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
|
|
||||||
params["status"] = text
|
|
||||||
if message.image == None:
|
|
||||||
params["call_name"] = "update_status"
|
|
||||||
else:
|
|
||||||
params["call_name"] = "update_status_with_media"
|
|
||||||
params["media"] = message.file
|
|
||||||
item = self.session.api_call(**params)
|
|
||||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
|
||||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
|
||||||
# if item != None:
|
|
||||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
|
||||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
if hasattr(message.message, "destroy"): message.message.destroy()
|
||||||
self.session.settings.write()
|
self.session.settings.write()
|
||||||
|
|
||||||
@ -477,25 +433,23 @@ class BaseBuffer(base.Buffer):
|
|||||||
else:
|
else:
|
||||||
screen_name = self.session.get_user(tweet.user).screen_name
|
screen_name = self.session.get_user(tweet.user).screen_name
|
||||||
users = utils.get_all_users(tweet, self.session)
|
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, _("Direct message to %s") % (screen_name,), _("New direct message"), users)
|
||||||
if dm.message.get_response() == widgetUtils.OK:
|
if dm.message.ShowModal() == widgetUtils.OK:
|
||||||
screen_name = dm.message.get("cb")
|
screen_name = dm.message.cb.GetValue()
|
||||||
user = self.session.get_user_by_screen_name(screen_name)
|
user = self.session.get_user_by_screen_name(screen_name)
|
||||||
recipient_id = user
|
recipient_id = user
|
||||||
text = dm.message.get_text()
|
text = dm.message.text.GetValue()
|
||||||
val = self.session.api_call(call_name="send_direct_message", recipient_id=recipient_id, text=text)
|
if len(dm.attachments) > 0:
|
||||||
if val != None:
|
attachment = dm.attachments[0]
|
||||||
sent_dms = self.session.db["sent_direct_messages"]
|
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
|
||||||
sent_dms.append(val)
|
|
||||||
else:
|
else:
|
||||||
sent_dms.insert(0, val)
|
attachment = None
|
||||||
self.session.db["sent_direct_messages"] = sent_dms
|
call_threaded(self.session.direct_message, text=text, recipient=recipient_id, attachment=attachment)
|
||||||
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()
|
||||||
|
|
||||||
@_tweets_exist
|
@_tweets_exist
|
||||||
def share_item(self, *args, **kwargs):
|
def share_item(self, *args, **kwargs):
|
||||||
|
if self.can_share() == False:
|
||||||
|
return output.speak(_("This action is not supported on protected accounts."))
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
id = tweet.id
|
id = tweet.id
|
||||||
if self.session.settings["general"]["retweet_mode"] == "ask":
|
if self.session.settings["general"]["retweet_mode"] == "ask":
|
||||||
@ -509,40 +463,20 @@ class BaseBuffer(base.Buffer):
|
|||||||
else:
|
else:
|
||||||
self._retweet_with_comment(tweet, id)
|
self._retweet_with_comment(tweet, id)
|
||||||
|
|
||||||
def _retweet_with_comment(self, tweet, id, comment=''):
|
def _retweet_with_comment(self, tweet, id):
|
||||||
# If quoting a retweet, let's quote the original tweet instead the retweet.
|
|
||||||
if hasattr(tweet, "retweeted_status"):
|
if hasattr(tweet, "retweeted_status"):
|
||||||
tweet = tweet.retweeted_status
|
tweet = tweet.retweeted_status
|
||||||
if hasattr(tweet, "full_text"):
|
retweet = messages.tweet(session=self.session, title=_("Quote"), caption=_("Add your comment to the tweet"), max=256, thread_mode=False)
|
||||||
comments = tweet.full_text
|
if retweet.message.ShowModal() == widgetUtils.OK:
|
||||||
else:
|
text = retweet.message.text.GetValue()
|
||||||
comments = tweet.text
|
tweet_data = dict(text=text, attachments=retweet.attachments, poll_period=retweet.poll_period, poll_options=retweet.poll_options)
|
||||||
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")
|
tweet_data.update(quote_tweet_id=id)
|
||||||
if comment != '':
|
call_threaded(self.session.send_tweet, *[tweet_data])
|
||||||
retweet.message.set_text(comment)
|
if hasattr(retweet.message, "destroy"):
|
||||||
if retweet.message.get_response() == widgetUtils.OK:
|
retweet.message.Destroy()
|
||||||
text = retweet.message.get_text()
|
|
||||||
text = text+" https://twitter.com/{0}/status/{1}".format(self.session.get_user(tweet.user).screen_name, id)
|
|
||||||
if retweet.image == None:
|
|
||||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
|
||||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
|
||||||
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:
|
|
||||||
# new_item = self.session.twitter.get_status(id=item.id, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
# pub.sendMessage("sent-tweet", data=new_item, user=self.session.db["user_name"])
|
|
||||||
else:
|
|
||||||
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, media=retweet.image)
|
|
||||||
if hasattr(retweet.message, "destroy"): retweet.message.destroy()
|
|
||||||
|
|
||||||
def _direct_retweet(self, id):
|
def _direct_retweet(self, id):
|
||||||
item = self.session.api_call(call_name="retweet", _sound="retweet_send.ogg", id=id)
|
item = self.session.api_call(call_name="retweet", _sound="retweet_send.ogg", id=id)
|
||||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
|
||||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
|
||||||
# if item != None:
|
|
||||||
# Retweets are returned as non-extended tweets, so let's get the object as extended
|
|
||||||
# just before sending the event message. See https://github.com/manuelcortez/TWBlue/issues/253
|
|
||||||
# item = self.session.twitter.get_status(id=item.id, include_ext_alt_text=True, tweet_mode="extended")
|
|
||||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
|
||||||
|
|
||||||
def onFocus(self, *args, **kwargs):
|
def onFocus(self, *args, **kwargs):
|
||||||
tweet = self.get_tweet()
|
tweet = self.get_tweet()
|
||||||
@ -557,6 +491,9 @@ class BaseBuffer(base.Buffer):
|
|||||||
self.session.sound.play("geo.ogg")
|
self.session.sound.play("geo.ogg")
|
||||||
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
|
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
|
||||||
self.session.sound.play("image.ogg")
|
self.session.sound.play("image.ogg")
|
||||||
|
can_share = self.can_share()
|
||||||
|
pub.sendMessage("toggleShare", shareable=can_share)
|
||||||
|
self.buffer.retweet.Enable(can_share)
|
||||||
|
|
||||||
def audio(self, url='', *args, **kwargs):
|
def audio(self, url='', *args, **kwargs):
|
||||||
if sound.URLPlayer.player.is_playing():
|
if sound.URLPlayer.player.is_playing():
|
||||||
|
@ -8,7 +8,7 @@ import config
|
|||||||
import languageHandler
|
import languageHandler
|
||||||
import logging
|
import logging
|
||||||
from controller import messages
|
from controller import messages
|
||||||
from sessions.twitter import compose, utils
|
from sessions.twitter import compose, utils, templates
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
@ -90,18 +90,12 @@ class DirectMessagesBuffer(base.BaseBuffer):
|
|||||||
def reply(self, *args, **kwargs):
|
def reply(self, *args, **kwargs):
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
screen_name = self.session.get_user(tweet.message_create["sender_id"]).screen_name
|
screen_name = self.session.get_user(tweet.message_create["sender_id"]).screen_name
|
||||||
message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,])
|
message = messages.reply(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False, users=[screen_name,])
|
||||||
if message.message.get_response() == widgetUtils.OK:
|
if message.message.ShowModal() == widgetUtils.OK:
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
tweet_data = message.get_tweet_data()
|
||||||
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
|
call_threaded(self.session.send_tweet, tweet_data)
|
||||||
config.app.write()
|
if hasattr(message.message, "destroy"):
|
||||||
if message.image == None:
|
message.message.destroy()
|
||||||
item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended")
|
|
||||||
if item != None:
|
|
||||||
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
|
||||||
else:
|
|
||||||
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
|
|
||||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
|
||||||
|
|
||||||
def onFocus(self, *args, **kwargs):
|
def onFocus(self, *args, **kwargs):
|
||||||
tweet = self.get_tweet()
|
tweet = self.get_tweet()
|
||||||
@ -135,6 +129,12 @@ class DirectMessagesBuffer(base.BaseBuffer):
|
|||||||
def open_in_browser(self, *args, **kwargs):
|
def open_in_browser(self, *args, **kwargs):
|
||||||
output.speak(_(u"This action is not supported in the buffer yet."))
|
output.speak(_(u"This action is not supported in the buffer yet."))
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
template = self.session.settings["templates"]["dm"]
|
||||||
|
dm = self.get_right_tweet()
|
||||||
|
t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
class SentDirectMessagesBuffer(DirectMessagesBuffer):
|
class SentDirectMessagesBuffer(DirectMessagesBuffer):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -157,3 +157,9 @@ class SentDirectMessagesBuffer(DirectMessagesBuffer):
|
|||||||
for i in items:
|
for i in items:
|
||||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||||
self.buffer.list.insert_item(False, *tweet)
|
self.buffer.list.insert_item(False, *tweet)
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
template = self.session.settings["templates"]["dm_sent"]
|
||||||
|
dm = self.get_right_tweet()
|
||||||
|
t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
@ -16,7 +16,7 @@ import logging
|
|||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from sessions.twitter import compose
|
from sessions.twitter import compose, templates
|
||||||
from . import base
|
from . import base
|
||||||
|
|
||||||
log = logging.getLogger("controller.buffers.twitter.peopleBuffer")
|
log = logging.getLogger("controller.buffers.twitter.peopleBuffer")
|
||||||
@ -84,7 +84,10 @@ class PeopleBuffer(base.BaseBuffer):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_message(self):
|
def get_message(self):
|
||||||
return " ".join(self.compose_function(self.get_tweet(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session))
|
template = self.session.settings["templates"]["person"]
|
||||||
|
user = self.get_right_tweet()
|
||||||
|
t = templates.render_person(user, template, self.session, relative_times=True, offset_seconds=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
def delete_item(self): pass
|
def delete_item(self): pass
|
||||||
|
|
||||||
@ -92,18 +95,12 @@ class PeopleBuffer(base.BaseBuffer):
|
|||||||
def reply(self, *args, **kwargs):
|
def reply(self, *args, **kwargs):
|
||||||
tweet = self.get_right_tweet()
|
tweet = self.get_right_tweet()
|
||||||
screen_name = tweet.screen_name
|
screen_name = tweet.screen_name
|
||||||
message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,])
|
message = messages.tweet(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False)
|
||||||
if message.message.get_response() == widgetUtils.OK:
|
if message.message.ShowModal() == widgetUtils.OK:
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
tweet_data = message.get_tweet_data()
|
||||||
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
|
call_threaded(self.session.send_tweet, tweet_data)
|
||||||
config.app.write()
|
if hasattr(message.message, "destroy"):
|
||||||
if message.image == None:
|
message.message.destroy()
|
||||||
item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended")
|
|
||||||
if item != None:
|
|
||||||
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
|
||||||
else:
|
|
||||||
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
|
|
||||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
|
||||||
|
|
||||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||||
# starts stream every 3 minutes.
|
# starts stream every 3 minutes.
|
||||||
|
@ -64,12 +64,16 @@ class SearchPeopleBuffer(people.PeopleBuffer):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
class ConversationBuffer(SearchBuffer):
|
class ConversationBuffer(SearchBuffer):
|
||||||
|
last_thread_id = None
|
||||||
|
last_reply_id = None
|
||||||
|
|
||||||
def start_stream(self, start=False, mandatory=False, play_sound=True, avoid_autoreading=False):
|
def start_stream(self, start=False, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
||||||
self.execution_time = current_time
|
self.execution_time = current_time
|
||||||
|
log.debug("Retrieving conversation. Last thread ID is {}, last reply ID is {}".format(self.last_thread_id, self.last_reply_id))
|
||||||
results = self.get_replies(self.tweet)
|
results = self.get_replies(self.tweet)
|
||||||
|
log.debug("Retrieved {} items before filters.".format(len(results)))
|
||||||
number_of_items = self.session.order_buffer(self.name, results)
|
number_of_items = self.session.order_buffer(self.name, results)
|
||||||
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)
|
||||||
@ -117,12 +121,12 @@ class ConversationBuffer(SearchBuffer):
|
|||||||
# find all tweets replying to the original thread only. Those tweets are sent by the same author who originally posted the first tweet.
|
# find all tweets replying to the original thread only. Those tweets are sent by the same author who originally posted the first tweet.
|
||||||
try:
|
try:
|
||||||
term = "conversation_id:{} from:{} to:{}".format(conversation_id, original_tweet.data.author_id, original_tweet.data.author_id)
|
term = "conversation_id:{} from:{} to:{}".format(conversation_id, original_tweet.data.author_id, original_tweet.data.author_id)
|
||||||
thread_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=98, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
|
thread_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=98, since_id=self.last_thread_id, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
|
||||||
if thread_tweets.data != None:
|
if thread_tweets.data != None:
|
||||||
thread_results.extend(thread_tweets.data)
|
thread_results.extend(thread_tweets.data)
|
||||||
# Search only replies to conversation_id.
|
# Search only replies to conversation_id.
|
||||||
term = "conversation_id:{}".format(conversation_id, original_tweet.data.author_id)
|
term = "conversation_id:{}".format(conversation_id, original_tweet.data.author_id)
|
||||||
reply_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=50, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
|
reply_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=50, since_id=self.last_reply_id, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
|
||||||
if reply_tweets.data != None:
|
if reply_tweets.data != None:
|
||||||
reply_results.extend(reply_tweets.data)
|
reply_results.extend(reply_tweets.data)
|
||||||
except TweepyException as e:
|
except TweepyException as e:
|
||||||
@ -135,6 +139,7 @@ class ConversationBuffer(SearchBuffer):
|
|||||||
try:
|
try:
|
||||||
thread_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
thread_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
||||||
thread_results.sort(key=lambda x: x.id)
|
thread_results.sort(key=lambda x: x.id)
|
||||||
|
self.last_thread_id = thread_results[-1].id
|
||||||
results.extend(thread_results)
|
results.extend(thread_results)
|
||||||
except TweepyException as e:
|
except TweepyException as e:
|
||||||
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
||||||
@ -144,7 +149,39 @@ class ConversationBuffer(SearchBuffer):
|
|||||||
try:
|
try:
|
||||||
reply_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
reply_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
||||||
reply_results.sort(key=lambda x: x.id)
|
reply_results.sort(key=lambda x: x.id)
|
||||||
|
self.last_reply_id = reply_results[-1].id
|
||||||
results.extend(reply_results)
|
results.extend(reply_results)
|
||||||
except TweepyException as e:
|
except TweepyException as e:
|
||||||
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
def get_replies_v1(self, tweet):
|
||||||
|
try:
|
||||||
|
tweet = self.session.twitter.get_status(id=tweet.id, tweet_mode="extended")
|
||||||
|
except:
|
||||||
|
log.exception("Error getting tweet for making a conversation buffer.")
|
||||||
|
return []
|
||||||
|
results = []
|
||||||
|
results.append(tweet)
|
||||||
|
if hasattr(tweet, "in_reply_to_status_id") and tweet.in_reply_to_status_id != None:
|
||||||
|
while tweet.in_reply_to_status_id != None:
|
||||||
|
original_tweet = self.session.twitter.get_status(id=tweet.in_reply_to_status_id, tweet_mode="extended")
|
||||||
|
results.insert(0, original_tweet)
|
||||||
|
tweet = original_tweet
|
||||||
|
try:
|
||||||
|
term = "from:{} to:{}".format(tweet.user.screen_name, tweet.user.screen_name)
|
||||||
|
thread_tweets = self.session.twitter.search_tweets(term, count=100, since_id=tweet.id, tweet_mode="extended")
|
||||||
|
results.extend(thread_tweets)
|
||||||
|
except TweepyException as e:
|
||||||
|
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
|
||||||
|
|
||||||
|
try:
|
||||||
|
term = "to:{}".format(tweet.user.screen_name)
|
||||||
|
reply_tweets = self.session.twitter.search_tweets(term, count=100, since_id=tweet.id, tweet_mode="extended")
|
||||||
|
ids = [t.id for t in results]
|
||||||
|
reply_tweets = [t for t in reply_tweets if hasattr(t, "in_reply_to_status_id") and t.in_reply_to_status_id in ids]
|
||||||
|
results.extend(reply_tweets)
|
||||||
|
except TweepyException as e:
|
||||||
|
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
|
||||||
|
results.sort(key=lambda x: x.id)
|
||||||
|
return results
|
@ -4,7 +4,7 @@ import platform
|
|||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
import wx
|
import wx
|
||||||
from wxUI import buffers, commonMessageDialogs, menus
|
from wxUI import buffers, commonMessageDialogs, menus
|
||||||
from controller import user
|
from controller import user, messages
|
||||||
elif platform.system() == "Linux":
|
elif platform.system() == "Linux":
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gtkUI import buffers, commonMessageDialogs
|
from gtkUI import buffers, commonMessageDialogs
|
||||||
@ -38,6 +38,18 @@ class TrendsBuffer(base.Buffer):
|
|||||||
self.get_formatted_message = self.get_message
|
self.get_formatted_message = self.get_message
|
||||||
self.reply = self.search_topic
|
self.reply = self.search_topic
|
||||||
|
|
||||||
|
|
||||||
|
def post_status(self, *args, **kwargs):
|
||||||
|
title = _("Tweet")
|
||||||
|
caption = _("Write the tweet here")
|
||||||
|
tweet = messages.tweet(self.session, title, caption, "")
|
||||||
|
response = tweet.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
tweet_data = tweet.get_tweet_data()
|
||||||
|
call_threaded(self.session.send_tweet, *tweet_data)
|
||||||
|
if hasattr(tweet.message, "destroy"):
|
||||||
|
tweet.message.destroy()
|
||||||
|
|
||||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||||
# starts stream every 3 minutes.
|
# starts stream every 3 minutes.
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
@ -119,21 +131,13 @@ class TrendsBuffer(base.Buffer):
|
|||||||
|
|
||||||
def tweet_about_this_trend(self, *args, **kwargs):
|
def tweet_about_this_trend(self, *args, **kwargs):
|
||||||
if self.buffer.list.get_count() == 0: return
|
if self.buffer.list.get_count() == 0: return
|
||||||
title = _(u"Tweet")
|
title = _("Tweet")
|
||||||
caption = _(u"Write the tweet here")
|
caption = _("Write the tweet here")
|
||||||
tweet = messages.tweet(self.session, title, caption, self.get_message()+ " ")
|
tweet = messages.tweet(session=self.session, title=title, caption=caption, text=self.get_message()+ " ")
|
||||||
tweet.message.set_cursor_at_end()
|
tweet.message.SetInsertionPoint(len(tweet.message.GetValue()))
|
||||||
if tweet.message.get_response() == widgetUtils.OK:
|
if tweet.message.ShowModal() == widgetUtils.OK:
|
||||||
text = tweet.message.get_text()
|
tweet_data = tweet.get_tweet_data()
|
||||||
if len(text) > 280 and tweet.message.get("long_tweet") == True:
|
call_threaded(self.session.send_tweet, *tweet_data)
|
||||||
if tweet.image == None:
|
|
||||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
|
|
||||||
else:
|
|
||||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
|
|
||||||
if tweet.image == None:
|
|
||||||
call_threaded(self.session.api_call, call_name="update_status", status=text)
|
|
||||||
else:
|
|
||||||
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image)
|
|
||||||
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
|
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
|
||||||
|
|
||||||
def show_menu_by_key(self, ev):
|
def show_menu_by_key(self, ev):
|
||||||
|
40
src/controller/editTemplateController.py
Normal file
40
src/controller/editTemplateController.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import wx
|
||||||
|
from typing import List
|
||||||
|
from sessions.twitter.templates import tweet_variables, dm_variables, person_variables
|
||||||
|
from wxUI.dialogs.twitterDialogs import templateDialogs
|
||||||
|
|
||||||
|
class EditTemplate(object):
|
||||||
|
def __init__(self, template: str, type: str) -> None:
|
||||||
|
super(EditTemplate, self).__init__()
|
||||||
|
self.default_template = template
|
||||||
|
if type == "tweet":
|
||||||
|
self.variables = tweet_variables
|
||||||
|
elif type == "dm":
|
||||||
|
self.variables = dm_variables
|
||||||
|
else:
|
||||||
|
self.variables = person_variables
|
||||||
|
self.template: str = template
|
||||||
|
|
||||||
|
def validate_template(self, template: str) -> bool:
|
||||||
|
used_variables: List[str] = re.findall("\$\w+", template)
|
||||||
|
validated: bool = True
|
||||||
|
for var in used_variables:
|
||||||
|
if var[1:] not in self.variables:
|
||||||
|
validated = False
|
||||||
|
return validated
|
||||||
|
|
||||||
|
def run_dialog(self) -> str:
|
||||||
|
dialog = templateDialogs.EditTemplateDialog(template=self.template, variables=self.variables, default_template=self.default_template)
|
||||||
|
response = dialog.ShowModal()
|
||||||
|
if response == wx.ID_SAVE:
|
||||||
|
validated: bool = self.validate_template(dialog.template.GetValue())
|
||||||
|
if validated == False:
|
||||||
|
templateDialogs.invalid_template()
|
||||||
|
self.template = dialog.template.GetValue()
|
||||||
|
return self.run_dialog()
|
||||||
|
else:
|
||||||
|
return dialog.template.GetValue()
|
||||||
|
else:
|
||||||
|
return ""
|
@ -134,6 +134,7 @@ class Controller(object):
|
|||||||
pub.subscribe(self.manage_blocked_user, "blocked-user")
|
pub.subscribe(self.manage_blocked_user, "blocked-user")
|
||||||
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
|
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
|
||||||
pub.subscribe(self.create_buffer, "createBuffer")
|
pub.subscribe(self.create_buffer, "createBuffer")
|
||||||
|
pub.subscribe(self.toggle_share_settings, "toggleShare")
|
||||||
if system == "Windows":
|
if system == "Windows":
|
||||||
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
|
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
|
||||||
@ -334,17 +335,17 @@ class Controller(object):
|
|||||||
root_position =self.view.search(session.db["user_name"], session.db["user_name"])
|
root_position =self.view.search(session.db["user_name"], session.db["user_name"])
|
||||||
for i in session.settings['general']['buffer_order']:
|
for i in session.settings['general']['buffer_order']:
|
||||||
if i == 'home':
|
if i == 'home':
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Home"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="home_timeline", name="home_timeline", sessionObject=session, account=session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Home"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="home_timeline", name="home_timeline", sessionObject=session, account=session.db["user_name"], sound="tweet_received.ogg", include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
elif i == 'mentions':
|
elif i == 'mentions':
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="mentions_timeline", name="mentions", sessionObject=session, account=session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="mentions_timeline", name="mentions", sessionObject=session, account=session.db["user_name"], sound="mention_received.ogg", include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
elif i == 'dm':
|
elif i == 'dm':
|
||||||
pub.sendMessage("createBuffer", buffer_type="DirectMessagesBuffer", session_type=session.type, buffer_title=_("Direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_direct_messages", name="direct_messages", sessionObject=session, account=session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg"))
|
pub.sendMessage("createBuffer", buffer_type="DirectMessagesBuffer", session_type=session.type, buffer_title=_("Direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_direct_messages", name="direct_messages", sessionObject=session, account=session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg"))
|
||||||
elif i == 'sent_dm':
|
elif i == 'sent_dm':
|
||||||
pub.sendMessage("createBuffer", buffer_type="SentDirectMessagesBuffer", session_type=session.type, buffer_title=_("Sent direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function=None, name="sent_direct_messages", sessionObject=session, account=session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message"))
|
pub.sendMessage("createBuffer", buffer_type="SentDirectMessagesBuffer", session_type=session.type, buffer_title=_("Sent direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function=None, name="sent_direct_messages", sessionObject=session, account=session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message"))
|
||||||
elif i == 'sent_tweets':
|
elif i == 'sent_tweets':
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent tweets"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="sent_tweets", sessionObject=session, account=session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent tweets"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="sent_tweets", sessionObject=session, account=session.db["user_name"], screen_name=session.db["user_name"], include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
elif i == 'favorites':
|
elif i == 'favorites':
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="favourites", sessionObject=session, account=session.db["user_name"], sound="favourite.ogg", tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="favourites", sessionObject=session, account=session.db["user_name"], sound="favourite.ogg", include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
elif i == 'followers':
|
elif i == 'followers':
|
||||||
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_followers", name="followers", sessionObject=session, account=session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"]))
|
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_followers", name="followers", sessionObject=session, account=session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"]))
|
||||||
elif i == 'friends':
|
elif i == 'friends':
|
||||||
@ -356,11 +357,11 @@ class Controller(object):
|
|||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="timelines", account=session.db["user_name"]))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="timelines", account=session.db["user_name"]))
|
||||||
timelines_position =self.view.search("timelines", session.db["user_name"])
|
timelines_position =self.view.search("timelines", session.db["user_name"])
|
||||||
for i in session.settings["other_buffers"]["timelines"]:
|
for i in session.settings["other_buffers"]["timelines"]:
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_(u"Timeline for {}").format(i,), parent_tab=timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="%s-timeline" % (i,), sessionObject=session, account=session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_(u"Timeline for {}").format(i,), parent_tab=timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="%s-timeline" % (i,), sessionObject=session, account=session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Likes timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="favs_timelines", account=session.db["user_name"]))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Likes timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="favs_timelines", account=session.db["user_name"]))
|
||||||
favs_timelines_position =self.view.search("favs_timelines", session.db["user_name"])
|
favs_timelines_position =self.view.search("favs_timelines", session.db["user_name"])
|
||||||
for i in session.settings["other_buffers"]["favourites_timelines"]:
|
for i in session.settings["other_buffers"]["favourites_timelines"]:
|
||||||
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes for {}").format(i,), parent_tab=favs_timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="%s-favorite" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes for {}").format(i,), parent_tab=favs_timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="%s-favorite" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Followers timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="followers_timelines", account=session.db["user_name"]))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Followers timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="followers_timelines", account=session.db["user_name"]))
|
||||||
followers_timelines_position =self.view.search("followers_timelines", session.db["user_name"])
|
followers_timelines_position =self.view.search("followers_timelines", session.db["user_name"])
|
||||||
for i in session.settings["other_buffers"]["followers_timelines"]:
|
for i in session.settings["other_buffers"]["followers_timelines"]:
|
||||||
@ -372,11 +373,11 @@ class Controller(object):
|
|||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Lists"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="lists", account=session.db["user_name"]))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Lists"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="lists", account=session.db["user_name"]))
|
||||||
lists_position =self.view.search("lists", session.db["user_name"])
|
lists_position =self.view.search("lists", session.db["user_name"])
|
||||||
for i in session.settings["other_buffers"]["lists"]:
|
for i in session.settings["other_buffers"]["lists"]:
|
||||||
pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=self.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=self.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Searches"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="searches", account=session.db["user_name"]))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Searches"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="searches", account=session.db["user_name"]))
|
||||||
searches_position =self.view.search("searches", session.db["user_name"])
|
searches_position =self.view.search("searches", session.db["user_name"])
|
||||||
for i in session.settings["other_buffers"]["tweet_searches"]:
|
for i in session.settings["other_buffers"]["tweet_searches"]:
|
||||||
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_(u"Search for {}").format(i), parent_tab=searches_position, start=False, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (i,), sessionObject=session, account=session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended"))
|
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_(u"Search for {}").format(i), parent_tab=searches_position, start=False, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (i,), sessionObject=session, account=session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, include_ext_alt_text=True, tweet_mode="extended"))
|
||||||
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
||||||
pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="%s_tt" % (i,), sessionObject=session, account=session.db["user_name"], trendsFor=i, sound="trends_updated.ogg"))
|
pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="%s_tt" % (i,), sessionObject=session, account=session.db["user_name"], trendsFor=i, sound="trends_updated.ogg"))
|
||||||
|
|
||||||
@ -423,7 +424,7 @@ class Controller(object):
|
|||||||
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
|
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
|
||||||
buffer.session.settings.write()
|
buffer.session.settings.write()
|
||||||
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
|
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
|
||||||
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=buffer.session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (term,), sessionObject=buffer.session, account=buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args))
|
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=buffer.session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (term,), sessionObject=buffer.session, account=buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, include_ext_alt_text=True, tweet_mode="extended", **args))
|
||||||
else:
|
else:
|
||||||
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
|
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
|
||||||
return
|
return
|
||||||
@ -872,7 +873,7 @@ class Controller(object):
|
|||||||
if usr.id_str in buff.session.settings["other_buffers"]["timelines"]:
|
if usr.id_str in buff.session.settings["other_buffers"]["timelines"]:
|
||||||
commonMessageDialogs.timeline_exist()
|
commonMessageDialogs.timeline_exist()
|
||||||
return
|
return
|
||||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, tweet_mode="extended")
|
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended")
|
||||||
try:
|
try:
|
||||||
tl.start_stream(play_sound=False)
|
tl.start_stream(play_sound=False)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -891,7 +892,7 @@ class Controller(object):
|
|||||||
if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]:
|
if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]:
|
||||||
commonMessageDialogs.timeline_exist()
|
commonMessageDialogs.timeline_exist()
|
||||||
return
|
return
|
||||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "%s-favorite" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, tweet_mode="extended")
|
tl = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "%s-favorite" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended")
|
||||||
try:
|
try:
|
||||||
tl.start_stream(play_sound=False)
|
tl.start_stream(play_sound=False)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -1088,10 +1089,10 @@ class Controller(object):
|
|||||||
if position == page.buffer.list.get_selected():
|
if position == page.buffer.list.get_selected():
|
||||||
page.session.sound.play("limit.ogg")
|
page.session.sound.play("limit.ogg")
|
||||||
|
|
||||||
try:
|
# try:
|
||||||
output.speak(page.get_message(), True)
|
output.speak(page.get_message(), True)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def down(self, *args, **kwargs):
|
def down(self, *args, **kwargs):
|
||||||
page = self.get_current_buffer()
|
page = self.get_current_buffer()
|
||||||
@ -1100,16 +1101,16 @@ class Controller(object):
|
|||||||
return
|
return
|
||||||
position = page.buffer.list.get_selected()
|
position = page.buffer.list.get_selected()
|
||||||
index = position+1
|
index = position+1
|
||||||
try:
|
# try:
|
||||||
page.buffer.list.select_item(index)
|
page.buffer.list.select_item(index)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
if position == page.buffer.list.get_selected():
|
if position == page.buffer.list.get_selected():
|
||||||
page.session.sound.play("limit.ogg")
|
page.session.sound.play("limit.ogg")
|
||||||
try:
|
# try:
|
||||||
output.speak(page.get_message(), True)
|
output.speak(page.get_message(), True)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def left(self, *args, **kwargs):
|
def left(self, *args, **kwargs):
|
||||||
buff = self.view.get_current_buffer_pos()
|
buff = self.view.get_current_buffer_pos()
|
||||||
@ -1202,18 +1203,18 @@ class Controller(object):
|
|||||||
def go_home(self):
|
def go_home(self):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
buffer.buffer.list.select_item(0)
|
buffer.buffer.list.select_item(0)
|
||||||
try:
|
# try:
|
||||||
output.speak(buffer.get_message(), True)
|
output.speak(buffer.get_message(), True)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def go_end(self):
|
def go_end(self):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
buffer.buffer.list.select_item(buffer.buffer.list.get_count()-1)
|
buffer.buffer.list.select_item(buffer.buffer.list.get_count()-1)
|
||||||
try:
|
# try:
|
||||||
output.speak(buffer.get_message(), True)
|
output.speak(buffer.get_message(), True)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def go_page_up(self):
|
def go_page_up(self):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
@ -1222,10 +1223,10 @@ class Controller(object):
|
|||||||
else:
|
else:
|
||||||
index = buffer.buffer.list.get_selected() - 20
|
index = buffer.buffer.list.get_selected() - 20
|
||||||
buffer.buffer.list.select_item(index)
|
buffer.buffer.list.select_item(index)
|
||||||
try:
|
# try:
|
||||||
output.speak(buffer.get_message(), True)
|
output.speak(buffer.get_message(), True)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def go_page_down(self):
|
def go_page_down(self):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
@ -1234,10 +1235,10 @@ class Controller(object):
|
|||||||
else:
|
else:
|
||||||
index = buffer.buffer.list.get_selected() + 20
|
index = buffer.buffer.list.get_selected() + 20
|
||||||
buffer.buffer.list.select_item(index)
|
buffer.buffer.list.select_item(index)
|
||||||
try:
|
# try:
|
||||||
output.speak(buffer.get_message(), True)
|
output.speak(buffer.get_message(), True)
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def url(self, *args, **kwargs):
|
def url(self, *args, **kwargs):
|
||||||
buffer = self.get_current_buffer()
|
buffer = self.get_current_buffer()
|
||||||
@ -1385,7 +1386,7 @@ class Controller(object):
|
|||||||
buff = self.search_buffer("home_timeline", account)
|
buff = self.search_buffer("home_timeline", account)
|
||||||
if create == True:
|
if create == True:
|
||||||
if buffer == "favourites":
|
if buffer == "favourites":
|
||||||
favourites = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"], tweet_mode="extended")
|
favourites = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"], include_ext_alt_text=True, tweet_mode="extended")
|
||||||
self.buffers.append(favourites)
|
self.buffers.append(favourites)
|
||||||
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||||
favourites.start_stream(play_sound=False)
|
favourites.start_stream(play_sound=False)
|
||||||
@ -1415,7 +1416,7 @@ class Controller(object):
|
|||||||
if create in buff.session.settings["other_buffers"]["lists"]:
|
if create in buff.session.settings["other_buffers"]["lists"]:
|
||||||
output.speak(_(u"This list is already opened"), True)
|
output.speak(_(u"This list is already opened"), True)
|
||||||
return
|
return
|
||||||
tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended")
|
tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended")
|
||||||
buff.session.lists.append(tl)
|
buff.session.lists.append(tl)
|
||||||
pos=self.view.search("lists", buff.session.db["user_name"])
|
pos=self.view.search("lists", buff.session.db["user_name"])
|
||||||
self.insert_buffer(tl, pos)
|
self.insert_buffer(tl, pos)
|
||||||
@ -1657,6 +1658,10 @@ class Controller(object):
|
|||||||
if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
||||||
self.notify(buffer.session, sound_to_play)
|
self.notify(buffer.session, sound_to_play)
|
||||||
|
|
||||||
|
def toggle_share_settings(self, shareable=True):
|
||||||
|
self.view.retweet.Enable(shareable)
|
||||||
|
|
||||||
|
|
||||||
def check_streams(self):
|
def check_streams(self):
|
||||||
if self.started == False:
|
if self.started == False:
|
||||||
return
|
return
|
||||||
|
@ -1,123 +1,69 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import re
|
import os
|
||||||
import platform
|
|
||||||
import arrow
|
import arrow
|
||||||
import languageHandler
|
import languageHandler
|
||||||
system = platform.system()
|
import wx
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import output
|
import output
|
||||||
import url_shortener
|
|
||||||
import sound
|
import sound
|
||||||
import config
|
import config
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from twitter_text import parse_tweet
|
from twitter_text import parse_tweet
|
||||||
if system == "Windows":
|
from wxUI.dialogs import twitterDialogs, urlList
|
||||||
from wxUI.dialogs import message, urlList
|
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from extra import translator, SpellChecker, autocompletionUsers
|
from extra import translator, SpellChecker, autocompletionUsers
|
||||||
from extra.AudioUploader import audioUploader
|
from extra.AudioUploader import audioUploader
|
||||||
elif system == "Linux":
|
|
||||||
from gtkUI.dialogs import message
|
|
||||||
from sessions.twitter import utils
|
from sessions.twitter import utils
|
||||||
from . import attach
|
|
||||||
|
|
||||||
class basicTweet(object):
|
class basicTweet(object):
|
||||||
""" This class handles the tweet main features. Other classes should derive from this class."""
|
""" This class handles the tweet main features. Other classes should derive from this class."""
|
||||||
def __init__(self, session, title, caption, text, messageType="tweet", max=280, *args, **kwargs):
|
def __init__(self, session, title, caption, text="", messageType="tweet", max=280, *args, **kwargs):
|
||||||
super(basicTweet, self).__init__()
|
super(basicTweet, self).__init__()
|
||||||
self.max = max
|
self.max = max
|
||||||
self.title = title
|
self.title = title
|
||||||
self.session = session
|
self.session = session
|
||||||
self.message = getattr(message, messageType)(title, caption, text, *args, **kwargs)
|
self.message = getattr(twitterDialogs, messageType)(title=title, caption=caption, message=text, *args, **kwargs)
|
||||||
|
self.message.text.SetValue(text)
|
||||||
|
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||||
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
|
widgetUtils.connect_event(self.message.add_audio, widgetUtils.BUTTON_PRESSED, self.attach)
|
||||||
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||||
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
|
widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
if hasattr(self.message, "add"):
|
||||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach)
|
||||||
if hasattr(self.message, "long_tweet"):
|
|
||||||
widgetUtils.connect_event(self.message.long_tweet, widgetUtils.CHECKBOX, self.text_processor)
|
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
|
||||||
self.message.long_tweet.SetValue(config.app["app-settings"]["longtweet"])
|
|
||||||
self.attachments = []
|
self.attachments = []
|
||||||
|
|
||||||
def translate(self, event=None):
|
def translate(self, event=None):
|
||||||
dlg = translator.gui.translateDialog()
|
dlg = translator.gui.translateDialog()
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
if dlg.get_response() == widgetUtils.OK:
|
||||||
text_to_translate = self.message.get_text()
|
text_to_translate = self.message.text.GetValue()
|
||||||
language_dict = translator.translator.available_languages()
|
language_dict = translator.translator.available_languages()
|
||||||
for k in language_dict:
|
for k in language_dict:
|
||||||
if language_dict[k] == dlg.dest_lang.GetStringSelection():
|
if language_dict[k] == dlg.dest_lang.GetStringSelection():
|
||||||
dst = k
|
dst = k
|
||||||
msg = translator.translator.translate(text=text_to_translate, target=dst)
|
msg = translator.translator.translate(text=text_to_translate, target=dst)
|
||||||
self.message.set_text(msg)
|
self.message.text.ChangeValue(msg)
|
||||||
|
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
self.message.text_focus()
|
self.message.text.SetFocus()
|
||||||
output.speak(_(u"Translated"))
|
output.speak(_(u"Translated"))
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
def shorten(self, event=None):
|
|
||||||
urls = utils.find_urls_in_text(self.message.get_text())
|
|
||||||
if len(urls) == 0:
|
|
||||||
output.speak(_(u"There's no URL to be shortened"))
|
|
||||||
self.message.text_focus()
|
|
||||||
elif len(urls) == 1:
|
|
||||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
|
|
||||||
output.speak(_(u"URL shortened"))
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text_focus()
|
|
||||||
elif len(urls) > 1:
|
|
||||||
list_urls = urlList.urlList()
|
|
||||||
list_urls.populate_list(urls)
|
|
||||||
if list_urls.get_response() == widgetUtils.OK:
|
|
||||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
|
|
||||||
output.speak(_(u"URL shortened"))
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text_focus()
|
|
||||||
|
|
||||||
def unshorten(self, event=None):
|
|
||||||
urls = utils.find_urls_in_text(self.message.get_text())
|
|
||||||
if len(urls) == 0:
|
|
||||||
output.speak(_(u"There's no URL to be expanded"))
|
|
||||||
self.message.text_focus()
|
|
||||||
elif len(urls) == 1:
|
|
||||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
|
|
||||||
output.speak(_(u"URL expanded"))
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text_focus()
|
|
||||||
elif len(urls) > 1:
|
|
||||||
list_urls = urlList.urlList()
|
|
||||||
list_urls.populate_list(urls)
|
|
||||||
if list_urls.get_response() == widgetUtils.OK:
|
|
||||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
|
|
||||||
output.speak(_(u"URL expanded"))
|
|
||||||
self.text_processor()
|
|
||||||
self.message.text_focus()
|
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
def text_processor(self, *args, **kwargs):
|
||||||
if len(self.message.get_text()) > 1:
|
text = self.message.text.GetValue()
|
||||||
self.message.enable_button("shortenButton")
|
|
||||||
self.message.enable_button("unshortenButton")
|
|
||||||
else:
|
|
||||||
self.message.disable_button("shortenButton")
|
|
||||||
self.message.disable_button("unshortenButton")
|
|
||||||
if self.message.get("long_tweet") == False and hasattr(self, "max"):
|
|
||||||
text = self.message.get_text()
|
|
||||||
results = parse_tweet(text)
|
results = parse_tweet(text)
|
||||||
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, results.weightedLength, self.max))
|
self.message.SetTitle(_("%s - %s of %d characters") % (self.title, results.weightedLength, self.max))
|
||||||
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:
|
|
||||||
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.text.GetValue()
|
||||||
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
||||||
if hasattr(checker, "fixed_text"):
|
if hasattr(checker, "fixed_text"):
|
||||||
self.message.set_text(checker.fixed_text)
|
self.message.text.ChangeValue(checker.fixed_text)
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
self.message.text_focus()
|
self.message.text.SetFocus()
|
||||||
|
|
||||||
def attach(self, *args, **kwargs):
|
def attach(self, *args, **kwargs):
|
||||||
def completed_callback(dlg):
|
def completed_callback(dlg):
|
||||||
@ -125,48 +71,170 @@ class basicTweet(object):
|
|||||||
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
|
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
|
||||||
dlg.uploaderDialog.destroy()
|
dlg.uploaderDialog.destroy()
|
||||||
if "sndup.net/" in url:
|
if "sndup.net/" in url:
|
||||||
self.message.set_text(self.message.get_text()+url+" #audio")
|
self.message.text.ChangeValue(self.message.text.GetValue()+url+" #audio")
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
else:
|
else:
|
||||||
commonMessageDialogs.common_error(url)
|
commonMessageDialogs.common_error(url)
|
||||||
|
|
||||||
dlg.cleanup()
|
dlg.cleanup()
|
||||||
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
|
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
|
||||||
self.message.text_focus()
|
self.message.text.SetFocus()
|
||||||
|
|
||||||
class tweet(basicTweet):
|
def can_attach(self):
|
||||||
def __init__(self, session, title, caption, text, max=280, messageType="tweet", *args, **kwargs):
|
if len(self.attachments) == 0:
|
||||||
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
|
return True
|
||||||
self.image = None
|
elif len(self.attachments) == 1 and (self.attachments[0]["type"] == "video" or self.attachments[0]["type"] == "gif"):
|
||||||
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
return False
|
||||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
elif len(self.attachments) < 4:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def on_attach(self, *args, **kwargs):
|
||||||
|
can_attach = self.can_attach()
|
||||||
|
menu = self.message.attach_menu(can_attach)
|
||||||
|
self.message.Bind(wx.EVT_MENU, self.on_attach_image, self.message.add_image)
|
||||||
|
self.message.Bind(wx.EVT_MENU, self.on_attach_video, self.message.add_video)
|
||||||
|
if hasattr(self.message, "add_poll"):
|
||||||
|
self.message.Bind(wx.EVT_MENU, self.on_attach_poll, self.message.add_poll)
|
||||||
|
self.message.PopupMenu(menu, self.message.add.GetPosition())
|
||||||
|
|
||||||
|
def on_attach_image(self, *args, **kwargs):
|
||||||
|
can_attach = self.can_attach()
|
||||||
|
video_or_gif_present = False
|
||||||
|
for a in self.attachments:
|
||||||
|
if a["type"] == "video" or a["type"] == "gif":
|
||||||
|
video_or_gif = True
|
||||||
|
break
|
||||||
|
if can_attach == False or video_or_gif_present == True:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
image, description = self.message.get_image()
|
||||||
|
if image != None:
|
||||||
|
if image.endswith("gif"):
|
||||||
|
image_type = "gif"
|
||||||
|
else:
|
||||||
|
image_type = "photo"
|
||||||
|
imageInfo = {"type": image_type, "file": image, "description": description}
|
||||||
|
if len(self.attachments) > 0 and image_type == "gif":
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
self.attachments.append(imageInfo)
|
||||||
|
self.message.add_item(item=[os.path.basename(imageInfo["file"]), imageInfo["type"], imageInfo["description"]])
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
|
|
||||||
def upload_image(self, *args, **kwargs):
|
def on_attach_video(self, *args, **kwargs):
|
||||||
a = attach.attach()
|
if len(self.attachments) > 0:
|
||||||
if len(a.attachments) != 0:
|
return self.message.unable_to_attach_file()
|
||||||
self.attachments = a.attachments
|
video = self.message.get_video()
|
||||||
|
if video != None:
|
||||||
|
videoInfo = {"type": "video", "file": video, "description": ""}
|
||||||
|
if len(self.attachments) > 0:
|
||||||
|
return self.message.unable_to_attach_file()
|
||||||
|
self.attachments.append(videoInfo)
|
||||||
|
self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]])
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
|
def on_attach_poll(self, *args, **kwargs):
|
||||||
|
dlg = twitterDialogs.poll()
|
||||||
|
if dlg.ShowModal() == wx.ID_OK:
|
||||||
|
self.poll_options = dlg.get_options()
|
||||||
|
self.poll_period = 60*24*dlg.period.GetValue()
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def remove_attachment(self, *args, **kwargs):
|
||||||
|
attachment = self.message.attachments.GetFocusedItem()
|
||||||
|
if attachment > -1 and len(self.attachments) > attachment:
|
||||||
|
self.attachments.pop(attachment)
|
||||||
|
self.message.remove_item(list_type="attachment")
|
||||||
|
self.text_processor()
|
||||||
|
self.message.text.SetFocus()
|
||||||
|
|
||||||
|
class tweet(basicTweet):
|
||||||
|
def __init__(self, session, title, caption, text="", max=280, messageType="tweet", *args, **kwargs):
|
||||||
|
self.thread = []
|
||||||
|
self.poll_options = None
|
||||||
|
self.poll_period = None
|
||||||
|
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
|
||||||
|
widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||||
|
if hasattr(self.message, "add_tweet"):
|
||||||
|
widgetUtils.connect_event(self.message.add_tweet, widgetUtils.BUTTON_PRESSED, self.add_tweet)
|
||||||
|
widgetUtils.connect_event(self.message.remove_tweet, widgetUtils.BUTTON_PRESSED, self.remove_tweet)
|
||||||
|
widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
def autocomplete_users(self, *args, **kwargs):
|
||||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||||
c.show_menu()
|
c.show_menu()
|
||||||
|
|
||||||
|
def add_tweet(self, event, update_gui=True, *args, **kwargs):
|
||||||
|
text = self.message.text.GetValue()
|
||||||
|
attachments = self.attachments[::]
|
||||||
|
tweetdata = dict(text=text, attachments=attachments, poll_options=self.poll_options, poll_period=self.poll_period)
|
||||||
|
self.thread.append(tweetdata)
|
||||||
|
self.attachments = []
|
||||||
|
self.poll_options = None
|
||||||
|
self.poll_period = None
|
||||||
|
if update_gui:
|
||||||
|
self.message.reset_controls()
|
||||||
|
self.message.add_item(item=[text, len(attachments)], list_type="tweet")
|
||||||
|
self.message.text.SetFocus()
|
||||||
|
self.text_processor()
|
||||||
|
|
||||||
|
def get_tweet_data(self):
|
||||||
|
self.add_tweet(event=None, update_gui=False)
|
||||||
|
return self.thread
|
||||||
|
|
||||||
|
def text_processor(self, *args, **kwargs):
|
||||||
|
super(tweet, self).text_processor(*args, **kwargs)
|
||||||
|
if len(self.thread) > 0:
|
||||||
|
self.message.tweets.Enable(True)
|
||||||
|
self.message.remove_tweet.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.tweets.Enable(False)
|
||||||
|
self.message.remove_tweet.Enable(False)
|
||||||
|
if len(self.attachments) > 0:
|
||||||
|
self.message.attachments.Enable(True)
|
||||||
|
self.message.remove_attachment.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.attachments.Enable(False)
|
||||||
|
self.message.remove_attachment.Enable(False)
|
||||||
|
if hasattr(self.message, "add_tweet"):
|
||||||
|
if len(self.message.text.GetValue()) > 0 or len(self.attachments) > 0:
|
||||||
|
self.message.add_tweet.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.add_tweet.Enable(False)
|
||||||
|
|
||||||
|
def remove_tweet(self, *args, **kwargs):
|
||||||
|
tweet = self.message.tweets.GetFocusedItem()
|
||||||
|
if tweet > -1 and len(self.thread) > tweet:
|
||||||
|
self.thread.pop(tweet)
|
||||||
|
self.message.remove_item(list_type="tweet")
|
||||||
|
self.text_processor()
|
||||||
|
self.message.text.SetFocus()
|
||||||
|
|
||||||
|
|
||||||
class reply(tweet):
|
class reply(tweet):
|
||||||
def __init__(self, session, title, caption, text, users=[], ids=[]):
|
def __init__(self, session, title, caption, text, users=[], ids=[]):
|
||||||
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
|
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
|
||||||
self.ids = ids
|
self.ids = ids
|
||||||
self.users = users
|
self.users = users
|
||||||
if len(users) > 0:
|
if len(users) > 0:
|
||||||
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all)
|
widgetUtils.connect_event(self.message.mention_all, widgetUtils.CHECKBOX, self.mention_all)
|
||||||
self.message.enable_button("mentionAll")
|
self.message.mention_all.Enable(True)
|
||||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||||
self.message.mentionAll.SetValue(config.app["app-settings"]["mention_all"])
|
self.message.mention_all.SetValue(config.app["app-settings"]["mention_all"])
|
||||||
self.mention_all()
|
self.mention_all()
|
||||||
self.message.set_cursor_at_end()
|
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
|
|
||||||
|
def text_processor(self, *args, **kwargs):
|
||||||
|
super(tweet, self).text_processor(*args, **kwargs)
|
||||||
|
if len(self.attachments) > 0:
|
||||||
|
self.message.attachments.Enable(True)
|
||||||
|
self.message.remove_attachment.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.attachments.Enable(False)
|
||||||
|
self.message.remove_attachment.Enable(False)
|
||||||
|
|
||||||
def mention_all(self, *args, **kwargs):
|
def mention_all(self, *args, **kwargs):
|
||||||
if self.message.mentionAll.GetValue() == True:
|
if self.message.mention_all.GetValue() == True:
|
||||||
for i in self.message.checkboxes:
|
for i in self.message.checkboxes:
|
||||||
i.SetValue(True)
|
i.SetValue(True)
|
||||||
i.Hide()
|
i.Hide()
|
||||||
@ -176,10 +244,10 @@ class reply(tweet):
|
|||||||
i.Show()
|
i.Show()
|
||||||
|
|
||||||
def get_ids(self):
|
def get_ids(self):
|
||||||
excluded_ids = ""
|
excluded_ids = []
|
||||||
for i in range(0, len(self.message.checkboxes)):
|
for i in range(0, len(self.message.checkboxes)):
|
||||||
if self.message.checkboxes[i].GetValue() == False:
|
if self.message.checkboxes[i].GetValue() == False:
|
||||||
excluded_ids = excluded_ids + "{0},".format(self.ids[i],)
|
excluded_ids.append(self.ids[i])
|
||||||
return excluded_ids
|
return excluded_ids
|
||||||
|
|
||||||
def get_people(self):
|
def get_people(self):
|
||||||
@ -190,20 +258,34 @@ class reply(tweet):
|
|||||||
return people
|
return people
|
||||||
|
|
||||||
class dm(basicTweet):
|
class dm(basicTweet):
|
||||||
def __init__(self, session, title, caption, text):
|
def __init__(self, session, title, caption, users):
|
||||||
super(dm, self).__init__(session, title, caption, text, messageType="dm", max=10000)
|
super(dm, self).__init__(session, title, caption, messageType="dm", max=10000, users=users)
|
||||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed)
|
widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed)
|
||||||
|
|
||||||
def user_changed(self, *args, **kwargs):
|
def user_changed(self, *args, **kwargs):
|
||||||
self.title = _("Direct message to %s") % (self.message.get_user())
|
self.title = _("Direct message to %s") % (self.message.cb.GetValue())
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
def autocomplete_users(self, *args, **kwargs):
|
||||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||||
c.show_menu("dm")
|
c.show_menu("dm")
|
||||||
|
|
||||||
|
def text_processor(self, *args, **kwargs):
|
||||||
|
super(dm, self).text_processor(*args, **kwargs)
|
||||||
|
if len(self.attachments) > 0:
|
||||||
|
self.message.attachments.Enable(True)
|
||||||
|
self.message.remove_attachment.Enable(True)
|
||||||
|
else:
|
||||||
|
self.message.attachments.Enable(False)
|
||||||
|
self.message.remove_attachment.Enable(False)
|
||||||
|
|
||||||
|
def can_attach(self):
|
||||||
|
if len(self.attachments) == 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
class viewTweet(basicTweet):
|
class viewTweet(basicTweet):
|
||||||
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""):
|
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""):
|
||||||
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
|
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
|
||||||
@ -264,31 +346,28 @@ class viewTweet(basicTweet):
|
|||||||
for z in tweet.retweeted_status.extended_entities["media"]:
|
for z in tweet.retweeted_status.extended_entities["media"]:
|
||||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||||
image_description.append(z["ext_alt_text"])
|
image_description.append(z["ext_alt_text"])
|
||||||
self.message = message.viewTweet(text, rt_count, favs_count, source, date)
|
self.message = twitterDialogs.viewTweet(text, rt_count, favs_count, source, date)
|
||||||
results = parse_tweet(text)
|
results = parse_tweet(text)
|
||||||
self.message.set_title(results.weightedLength)
|
self.message.set_title(results.weightedLength)
|
||||||
[self.message.set_image_description(i) for i in image_description]
|
[self.message.set_image_description(i) for i in image_description]
|
||||||
else:
|
else:
|
||||||
self.title = _(u"View item")
|
self.title = _(u"View item")
|
||||||
text = tweet
|
text = tweet
|
||||||
self.message = message.viewNonTweet(text, date)
|
self.message = twitterDialogs.viewNonTweet(text, date)
|
||||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||||
if item_url != "":
|
if item_url != "":
|
||||||
self.message.enable_button("share")
|
self.message.enable_button("share")
|
||||||
widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share)
|
widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share)
|
||||||
self.item_url = item_url
|
self.item_url = item_url
|
||||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||||
if self.contain_urls() == True:
|
self.message.ShowModal()
|
||||||
self.message.enable_button("unshortenButton")
|
|
||||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
|
||||||
self.message.get_response()
|
|
||||||
|
|
||||||
def contain_urls(self):
|
# We won't need text_processor in this dialog, so let's avoid it.
|
||||||
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
|
def text_processor(self):
|
||||||
return True
|
pass
|
||||||
return False
|
|
||||||
|
|
||||||
def clear_text(self, text):
|
def clear_text(self, text):
|
||||||
|
text = utils.StripChars(text)
|
||||||
urls = utils.find_urls_in_text(text)
|
urls = utils.find_urls_in_text(text)
|
||||||
for i in urls:
|
for i in urls:
|
||||||
if "https://twitter.com/" in i:
|
if "https://twitter.com/" in i:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
import logging
|
||||||
import sound_lib
|
import sound_lib
|
||||||
import paths
|
import paths
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
@ -8,17 +9,18 @@ import config
|
|||||||
import languageHandler
|
import languageHandler
|
||||||
import output
|
import output
|
||||||
import application
|
import application
|
||||||
|
import config_utils
|
||||||
|
import keys
|
||||||
|
from collections import OrderedDict
|
||||||
|
from pubsub import pub
|
||||||
|
from mysc import autostart as autostart_windows
|
||||||
from wxUI.dialogs import configuration
|
from wxUI.dialogs import configuration
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from extra.autocompletionUsers import settings
|
from extra.autocompletionUsers import settings
|
||||||
from extra.ocr import OCRSpace
|
from extra.ocr import OCRSpace
|
||||||
from pubsub import pub
|
from .editTemplateController import EditTemplate
|
||||||
import logging
|
|
||||||
import config_utils
|
|
||||||
log = logging.getLogger("Settings")
|
log = logging.getLogger("Settings")
|
||||||
import keys
|
|
||||||
from collections import OrderedDict
|
|
||||||
from mysc import autostart as autostart_windows
|
|
||||||
|
|
||||||
class globalSettingsController(object):
|
class globalSettingsController(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -90,10 +92,12 @@ class globalSettingsController(object):
|
|||||||
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
|
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
|
||||||
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to interface language changes.")
|
||||||
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
|
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
|
||||||
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
|
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
|
||||||
kmFile = open(os.path.join(paths.config_path(), "keymap.keymap"), "w")
|
kmFile = open(os.path.join(paths.config_path(), "keymap.keymap"), "w")
|
||||||
kmFile.close()
|
kmFile.close()
|
||||||
|
log.debug("Triggered app restart due to a keymap change.")
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
|
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
|
||||||
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
|
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
|
||||||
@ -104,9 +108,11 @@ class globalSettingsController(object):
|
|||||||
if config.app["app-settings"]["no_streaming"] != self.dialog.get_value("general", "no_streaming"):
|
if config.app["app-settings"]["no_streaming"] != self.dialog.get_value("general", "no_streaming"):
|
||||||
config.app["app-settings"]["no_streaming"] = self.dialog.get_value("general", "no_streaming")
|
config.app["app-settings"]["no_streaming"] = self.dialog.get_value("general", "no_streaming")
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to change in streaming availability.")
|
||||||
if config.app["app-settings"]["update_period"] != self.dialog.get_value("general", "update_period"):
|
if config.app["app-settings"]["update_period"] != self.dialog.get_value("general", "update_period"):
|
||||||
config.app["app-settings"]["update_period"] = self.dialog.get_value("general", "update_period")
|
config.app["app-settings"]["update_period"] = self.dialog.get_value("general", "update_period")
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to changes in update period.")
|
||||||
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
||||||
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
||||||
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
||||||
@ -118,6 +124,7 @@ class globalSettingsController(object):
|
|||||||
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
||||||
if self.is_started == True:
|
if self.is_started == True:
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to change in proxy settings.")
|
||||||
config.app["proxy"]["type"] = self.dialog.proxy.type.Selection
|
config.app["proxy"]["type"] = self.dialog.proxy.type.Selection
|
||||||
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
|
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
|
||||||
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
|
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
|
||||||
@ -152,6 +159,15 @@ class accountSettingsController(globalSettingsController):
|
|||||||
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"])
|
||||||
|
tweet_template = self.config["templates"]["tweet"]
|
||||||
|
dm_template = self.config["templates"]["dm"]
|
||||||
|
sent_dm_template = self.config["templates"]["dm_sent"]
|
||||||
|
person_template = self.config["templates"]["person"]
|
||||||
|
self.dialog.create_templates(tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template)
|
||||||
|
widgetUtils.connect_event(self.dialog.templates.tweet, widgetUtils.BUTTON_PRESSED, self.edit_tweet_template)
|
||||||
|
widgetUtils.connect_event(self.dialog.templates.dm, widgetUtils.BUTTON_PRESSED, self.edit_dm_template)
|
||||||
|
widgetUtils.connect_event(self.dialog.templates.sent_dm, widgetUtils.BUTTON_PRESSED, self.edit_sent_dm_template)
|
||||||
|
widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template)
|
||||||
self.dialog.create_other_buffers()
|
self.dialog.create_other_buffers()
|
||||||
buffer_values = self.get_buffers_list()
|
buffer_values = self.get_buffers_list()
|
||||||
self.dialog.buffers.insert_buffers(buffer_values)
|
self.dialog.buffers.insert_buffers(buffer_values)
|
||||||
@ -160,7 +176,6 @@ class accountSettingsController(globalSettingsController):
|
|||||||
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
|
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
|
||||||
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
|
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
|
||||||
|
|
||||||
|
|
||||||
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
|
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
|
||||||
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
|
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
|
||||||
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
|
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
|
||||||
@ -185,15 +200,53 @@ class accountSettingsController(globalSettingsController):
|
|||||||
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
|
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
|
||||||
self.response = self.dialog.get_response()
|
self.response = self.dialog.get_response()
|
||||||
|
|
||||||
|
def edit_tweet_template(self, *args, **kwargs):
|
||||||
|
template = self.config["templates"]["tweet"]
|
||||||
|
control = EditTemplate(template=template, type="tweet")
|
||||||
|
result = control.run_dialog()
|
||||||
|
if result != "": # Template has been saved.
|
||||||
|
self.config["templates"]["tweet"] = result
|
||||||
|
self.config.write()
|
||||||
|
self.dialog.templates.tweet.SetLabel(_("Edit template for tweets. Current template: {}").format(result))
|
||||||
|
|
||||||
|
def edit_dm_template(self, *args, **kwargs):
|
||||||
|
template = self.config["templates"]["dm"]
|
||||||
|
control = EditTemplate(template=template, type="dm")
|
||||||
|
result = control.run_dialog()
|
||||||
|
if result != "": # Template has been saved.
|
||||||
|
self.config["templates"]["dm"] = result
|
||||||
|
self.config.write()
|
||||||
|
self.dialog.templates.dm.SetLabel(_("Edit template for direct messages. Current template: {}").format(result))
|
||||||
|
|
||||||
|
def edit_sent_dm_template(self, *args, **kwargs):
|
||||||
|
template = self.config["templates"]["dm_sent"]
|
||||||
|
control = EditTemplate(template=template, type="dm")
|
||||||
|
result = control.run_dialog()
|
||||||
|
if result != "": # Template has been saved.
|
||||||
|
self.config["templates"]["dm_sent"] = result
|
||||||
|
self.config.write()
|
||||||
|
self.dialog.templates.sent_dm.SetLabel(_("Edit template for sent direct messages. Current template: {}").format(result))
|
||||||
|
|
||||||
|
def edit_person_template(self, *args, **kwargs):
|
||||||
|
template = self.settings["templates"]["person"]
|
||||||
|
control = EditTemplate(template=template, type="person")
|
||||||
|
result = control.run_dialog()
|
||||||
|
if result != "": # Template has been saved.
|
||||||
|
self.config["templates"]["person"] = result
|
||||||
|
self.config.write()
|
||||||
|
self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result))
|
||||||
|
|
||||||
def save_configuration(self):
|
def save_configuration(self):
|
||||||
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to change in relative times.")
|
||||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
self.config["general"]["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"):
|
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.config["general"]["load_cache_in_memory"] = self.dialog.get_value("general", "load_cache_in_memory")
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to change in database strategy management.")
|
||||||
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
if self.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
|
||||||
@ -206,6 +259,7 @@ class accountSettingsController(globalSettingsController):
|
|||||||
|
|
||||||
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
|
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to change in timeline order.")
|
||||||
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
|
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
|
||||||
rt = self.dialog.get_value("general", "retweet_mode")
|
rt = self.dialog.get_value("general", "retweet_mode")
|
||||||
if rt == _(u"Ask"):
|
if rt == _(u"Ask"):
|
||||||
@ -217,28 +271,11 @@ class accountSettingsController(globalSettingsController):
|
|||||||
buffers_list = self.dialog.buffers.get_list()
|
buffers_list = self.dialog.buffers.get_list()
|
||||||
if buffers_list != self.config["general"]["buffer_order"]:
|
if buffers_list != self.config["general"]["buffer_order"]:
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
log.debug("Triggered app restart due to change in buffer ordering.")
|
||||||
self.config["general"]["buffer_order"] = buffers_list
|
self.config["general"]["buffer_order"] = buffers_list
|
||||||
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
|
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
|
||||||
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
|
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
|
||||||
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
|
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
|
||||||
# if self.config["other_buffers"]["show_followers"] != self.dialog.get_value("buffers", "followers"):
|
|
||||||
# self.config["other_buffers"]["show_followers"] = self.dialog.get_value("buffers", "followers")
|
|
||||||
# pub.sendMessage("create-new-buffer", buffer="followers", account=self.user, create=self.config["other_buffers"]["show_followers"])
|
|
||||||
# if self.config["other_buffers"]["show_friends"] != self.dialog.get_value("buffers", "friends"):
|
|
||||||
# self.config["other_buffers"]["show_friends"] = self.dialog.get_value("buffers", "friends")
|
|
||||||
# pub.sendMessage("create-new-buffer", buffer="friends", account=self.user, create=self.config["other_buffers"]["show_friends"])
|
|
||||||
# if self.config["other_buffers"]["show_favourites"] != self.dialog.get_value("buffers", "favs"):
|
|
||||||
# self.config["other_buffers"]["show_favourites"] = self.dialog.get_value("buffers", "favs")
|
|
||||||
# pub.sendMessage("create-new-buffer", buffer="favourites", account=self.user, create=self.config["other_buffers"]["show_favourites"])
|
|
||||||
# if self.config["other_buffers"]["show_blocks"] != self.dialog.get_value("buffers", "blocks"):
|
|
||||||
# self.config["other_buffers"]["show_blocks"] = self.dialog.get_value("buffers", "blocks")
|
|
||||||
# pub.sendMessage("create-new-buffer", buffer="blocked", account=self.user, create=self.config["other_buffers"]["show_blocks"])
|
|
||||||
# if self.config["other_buffers"]["show_muted_users"] != self.dialog.get_value("buffers", "mutes"):
|
|
||||||
# self.config["other_buffers"]["show_muted_users"] = self.dialog.get_value("buffers", "mutes")
|
|
||||||
# pub.sendMessage("create-new-buffer", buffer="muted", account=self.user, create=self.config["other_buffers"]["show_muted_users"])
|
|
||||||
# if self.config["other_buffers"]["show_events"] != self.dialog.get_value("buffers", "events"):
|
|
||||||
# self.config["other_buffers"]["show_events"] = self.dialog.get_value("buffers", "events")
|
|
||||||
# pub.sendMessage("create-new-buffer", buffer="events", account=self.user, create=self.config["other_buffers"]["show_events"])
|
|
||||||
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
|
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
|
||||||
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
|
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
|
||||||
try:
|
try:
|
||||||
|
@ -10,9 +10,9 @@ class autocompletionUsers(object):
|
|||||||
self.db = storage.storage(session_id)
|
self.db = storage.storage(session_id)
|
||||||
|
|
||||||
def show_menu(self, mode="tweet"):
|
def show_menu(self, mode="tweet"):
|
||||||
position = self.window.get_position()
|
position = self.window.text.GetInsertionPoint()
|
||||||
if mode == "tweet":
|
if mode == "tweet":
|
||||||
text = self.window.get_text()
|
text = self.window.text.GetValue()
|
||||||
text = text[:position]
|
text = text[:position]
|
||||||
try:
|
try:
|
||||||
pattern = text.split()[-1]
|
pattern = text.split()[-1]
|
||||||
@ -24,14 +24,14 @@ class autocompletionUsers(object):
|
|||||||
users = self.db.get_users(pattern[1:])
|
users = self.db.get_users(pattern[1:])
|
||||||
if len(users) > 0:
|
if len(users) > 0:
|
||||||
menu.append_options(users)
|
menu.append_options(users)
|
||||||
self.window.popup_menu(menu)
|
self.window.PopupMenu(menu, self.window.text.GetPosition())
|
||||||
menu.destroy()
|
menu.destroy()
|
||||||
else:
|
else:
|
||||||
output.speak(_(u"There are no results in your users database"))
|
output.speak(_(u"There are no results in your users database"))
|
||||||
else:
|
else:
|
||||||
output.speak(_(u"Autocompletion only works for users."))
|
output.speak(_(u"Autocompletion only works for users."))
|
||||||
elif mode == "dm":
|
elif mode == "dm":
|
||||||
text = self.window.get_user()
|
text = self.window.cb.GetValue()
|
||||||
try:
|
try:
|
||||||
pattern = text.split()[-1]
|
pattern = text.split()[-1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -41,7 +41,7 @@ class autocompletionUsers(object):
|
|||||||
users = self.db.get_users(pattern)
|
users = self.db.get_users(pattern)
|
||||||
if len(users) > 0:
|
if len(users) > 0:
|
||||||
menu.append_options(users)
|
menu.append_options(users)
|
||||||
self.window.popup_menu(menu)
|
self.window.PopupMenu(menu, self.window.text.GetPosition())
|
||||||
menu.destroy()
|
menu.destroy()
|
||||||
else:
|
else:
|
||||||
output.speak(_(u"There are no results in your users database"))
|
output.speak(_(u"There are no results in your users database"))
|
||||||
|
@ -42,7 +42,7 @@ toggle_buffer_mute = string(default="alt+win+shift+m")
|
|||||||
toggle_session_mute = string(default="control+alt+win+m")
|
toggle_session_mute = string(default="control+alt+win+m")
|
||||||
toggle_autoread = string(default="alt+win+e")
|
toggle_autoread = string(default="alt+win+e")
|
||||||
search = string(default="alt+win+-")
|
search = string(default="alt+win+-")
|
||||||
edit_keystrokes = string(default="alt+win+k")
|
edit_keystrokes = string(default="control+alt+win+k")
|
||||||
view_user_lists = string(default="alt+win+l")
|
view_user_lists = string(default="alt+win+l")
|
||||||
get_more_items = string(default="alt+win+pageup")
|
get_more_items = string(default="alt+win+pageup")
|
||||||
reverse_geocode = string(default="control+win+g")
|
reverse_geocode = string(default="control+win+g")
|
||||||
|
Binary file not shown.
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.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -5,8 +5,8 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: TwBlue 0.80\n"
|
"Project-Id-Version: TwBlue 0.80\n"
|
||||||
"POT-Creation-Date: 2021-10-28 16:43+Central Europe Daylight Time\n"
|
"POT-Creation-Date: 2021-11-11 01:16+0100\n"
|
||||||
"PO-Revision-Date: 2021-10-28 17:03+0100\n"
|
"PO-Revision-Date: 2021-11-11 01:30+0100\n"
|
||||||
"Last-Translator: Nikola Jović <wwenikola123@gmail.com>\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"
|
||||||
@ -18,10 +18,6 @@ msgstr ""
|
|||||||
"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"
|
||||||
|
|
||||||
#: ../src\controller\attach.py:25
|
|
||||||
msgid "Photo"
|
|
||||||
msgstr "Slika"
|
|
||||||
|
|
||||||
#: ../src\controller\buffers\base\base.py:91
|
#: ../src\controller\buffers\base\base.py:91
|
||||||
msgid "This action is not supported for this buffer"
|
msgid "This action is not supported for this buffer"
|
||||||
msgstr "Ova radnja nije podržana na ovom kanalu"
|
msgstr "Ova radnja nije podržana na ovom kanalu"
|
||||||
@ -100,101 +96,104 @@ msgstr "Prijatelji korisnika {username}"
|
|||||||
msgid "Unknown buffer"
|
msgid "Unknown buffer"
|
||||||
msgstr "Nepoznat kanal"
|
msgstr "Nepoznat kanal"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:88
|
#: ../src\controller\buffers\twitter\base.py:87
|
||||||
#: ../src\controller\buffers\twitter\trends.py:121
|
#: ../src\controller\buffers\twitter\trends.py:43
|
||||||
#: ../src\controller\messages.py:214 ../src\wxUI\buffers\base.py:25
|
#: ../src\controller\buffers\twitter\trends.py:134
|
||||||
|
#: ../src\controller\messages.py:296 ../src\wxUI\buffers\base.py:25
|
||||||
#: ../src\wxUI\buffers\events.py:15 ../src\wxUI\buffers\trends.py:18
|
#: ../src\wxUI\buffers\events.py:15 ../src\wxUI\buffers\trends.py:18
|
||||||
#: ../src\wxUI\dialogs\message.py:304 ../src\wxUI\sysTrayIcon.py:35
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:312
|
||||||
|
#: ../src\wxUI\sysTrayIcon.py:35
|
||||||
msgid "Tweet"
|
msgid "Tweet"
|
||||||
msgstr "Tvit"
|
msgstr "Tvit"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:89
|
#: ../src\controller\buffers\twitter\base.py:88
|
||||||
#: ../src\controller\buffers\twitter\trends.py:122
|
#: ../src\controller\buffers\twitter\trends.py:44
|
||||||
|
#: ../src\controller\buffers\twitter\trends.py:135
|
||||||
msgid "Write the tweet here"
|
msgid "Write the tweet here"
|
||||||
msgstr "Otkucajte tvit ovde:"
|
msgstr "Otkucajte tvit ovde:"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:219
|
#: ../src\controller\buffers\twitter\base.py:192
|
||||||
msgid "New tweet in {0}"
|
msgid "New tweet in {0}"
|
||||||
msgstr "Novi tvit u kanalu {0}"
|
msgstr "Novi tvit u kanalu {0}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:222
|
#: ../src\controller\buffers\twitter\base.py:195
|
||||||
msgid "{0} new tweets in {1}."
|
msgid "{0} new tweets in {1}."
|
||||||
msgstr "{0} novih tvitova u kanalu {1}."
|
msgstr "{0} novih tvitova u kanalu {1}."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:261
|
#: ../src\controller\buffers\twitter\base.py:234
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:87
|
#: ../src\controller\buffers\twitter\directMessages.py:88
|
||||||
#: ../src\controller\buffers\twitter\people.py:180
|
#: ../src\controller\buffers\twitter\people.py:174
|
||||||
msgid "%s items retrieved"
|
msgid "%s items retrieved"
|
||||||
msgstr "%s primljenih stavki"
|
msgstr "%s primljenih stavki"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:293
|
#: ../src\controller\buffers\twitter\base.py:266
|
||||||
#: ../src\controller\buffers\twitter\people.py:80
|
#: ../src\controller\buffers\twitter\people.py:80
|
||||||
msgid "This buffer is not a timeline; it can't be deleted."
|
msgid "This buffer is not a timeline; it can't be deleted."
|
||||||
msgstr "Ovaj kanal nije vremenska linija i ne može biti izbrisan."
|
msgstr "Ovaj kanal nije vremenska linija i ne može biti izbrisan."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:430
|
#: ../src\controller\buffers\twitter\base.py:402
|
||||||
msgid "Reply to {arg0}"
|
msgid "Reply to {arg0}"
|
||||||
msgstr "Odgovori {arg0}"
|
msgstr "Odgovori {arg0}"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:432
|
#: ../src\controller\buffers\twitter\base.py:404
|
||||||
#: ../src\keystrokeEditor\constants.py:11 ../src\wxUI\buffers\base.py:27
|
#: ../src\keystrokeEditor\constants.py:11 ../src\wxUI\buffers\base.py:27
|
||||||
msgid "Reply"
|
msgid "Reply"
|
||||||
msgstr "Odgovori"
|
msgstr "Odgovori"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:433
|
#: ../src\controller\buffers\twitter\base.py:405
|
||||||
msgid "Reply to %s"
|
msgid "Reply to %s"
|
||||||
msgstr "Odgovori %s"
|
msgstr "Odgovori %s"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:480
|
#: ../src\controller\buffers\twitter\base.py:428
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:129
|
#: ../src\controller\buffers\twitter\directMessages.py:124
|
||||||
msgid "New direct message"
|
msgid "New direct message"
|
||||||
msgstr "Nova direktna poruka"
|
msgstr "Nova direktna poruka"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:480
|
#: ../src\controller\buffers\twitter\base.py:428
|
||||||
#: ../src\controller\messages.py:200
|
#: ../src\controller\messages.py:268
|
||||||
msgid "Direct message to %s"
|
msgid "Direct message to %s"
|
||||||
msgstr "Direktna poruka za %s"
|
msgstr "Direktna poruka za %s"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:520
|
#: ../src\controller\buffers\twitter\base.py:459
|
||||||
msgid "Add your comment to the tweet"
|
msgid "Add your comment to the tweet"
|
||||||
msgstr "Dodajte vaš komentar u tvit"
|
msgstr "Dodajte vaš komentar u tvit"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:520
|
#: ../src\controller\buffers\twitter\base.py:459
|
||||||
msgid "Quote"
|
msgid "Quote"
|
||||||
msgstr "Citiraj"
|
msgstr "Citiraj"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:596
|
#: ../src\controller\buffers\twitter\base.py:520
|
||||||
msgid "Opening URL..."
|
msgid "Opening URL..."
|
||||||
msgstr "Otvaram vezu..."
|
msgstr "Otvaram vezu..."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:633
|
#: ../src\controller\buffers\twitter\base.py:557
|
||||||
msgid "User details"
|
msgid "User details"
|
||||||
msgstr "Podaci o korisniku"
|
msgstr "Podaci o korisniku"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\base.py:654
|
#: ../src\controller\buffers\twitter\base.py:578
|
||||||
msgid "Opening item in web browser..."
|
msgid "Opening item in web browser..."
|
||||||
msgstr "Otvaram stavku u Web pretraživaču..."
|
msgstr "Otvaram stavku u Web pretraživaču..."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:92
|
#: ../src\controller\buffers\twitter\directMessages.py:93
|
||||||
#: ../src\controller\buffers\twitter\people.py:95
|
#: ../src\controller\buffers\twitter\people.py:95
|
||||||
msgid "Mention to %s"
|
msgid "Mention to %s"
|
||||||
msgstr "Spomeni %s"
|
msgstr "Spomeni %s"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:92
|
#: ../src\controller\buffers\twitter\directMessages.py:93
|
||||||
#: ../src\controller\buffers\twitter\people.py:95
|
#: ../src\controller\buffers\twitter\people.py:95
|
||||||
#: ../src\wxUI\buffers\people.py:17
|
#: ../src\wxUI\buffers\people.py:17
|
||||||
msgid "Mention"
|
msgid "Mention"
|
||||||
msgstr "Spomeni"
|
msgstr "Spomeni"
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:132
|
#: ../src\controller\buffers\twitter\directMessages.py:127
|
||||||
msgid "{0} new direct messages."
|
msgid "{0} new direct messages."
|
||||||
msgstr "{0} novih direktnih poruka."
|
msgstr "{0} novih direktnih poruka."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:135
|
#: ../src\controller\buffers\twitter\directMessages.py:130
|
||||||
msgid "This action is not supported in the buffer yet."
|
msgid "This action is not supported in the buffer yet."
|
||||||
msgstr "Ova radnja još uvek nije podržana na ovom kanalu."
|
msgstr "Ova radnja još uvek nije podržana na ovom kanalu."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\directMessages.py:145
|
#: ../src\controller\buffers\twitter\directMessages.py:140
|
||||||
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."
|
||||||
@ -202,11 +201,11 @@ msgstr ""
|
|||||||
"Nemoguće preuzeti dodatne stavke za ovaj kanal. Umesto toga, koristite kanal "
|
"Nemoguće preuzeti dodatne stavke za ovaj kanal. Umesto toga, koristite kanal "
|
||||||
"direktne poruke."
|
"direktne poruke."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\people.py:253
|
#: ../src\controller\buffers\twitter\people.py:247
|
||||||
msgid "{0} new followers."
|
msgid "{0} new followers."
|
||||||
msgstr "{0} novih pratilaca."
|
msgstr "{0} novih pratilaca."
|
||||||
|
|
||||||
#: ../src\controller\buffers\twitter\trends.py:145
|
#: ../src\controller\buffers\twitter\trends.py:150
|
||||||
msgid "This action is not supported in the buffer, yet."
|
msgid "This action is not supported in the buffer, yet."
|
||||||
msgstr "Ova radnja još uvek nije podržana na ovom kanalu"
|
msgstr "Ova radnja još uvek nije podržana na ovom kanalu"
|
||||||
|
|
||||||
@ -224,7 +223,7 @@ msgstr "Vremenske linije"
|
|||||||
|
|
||||||
#: ../src\controller\mainController.py:359
|
#: ../src\controller\mainController.py:359
|
||||||
#: ../src\controller\mainController.py:883
|
#: ../src\controller\mainController.py:883
|
||||||
#: ../src\controller\mainController.py:1585
|
#: ../src\controller\mainController.py:1582
|
||||||
msgid "Timeline for {}"
|
msgid "Timeline for {}"
|
||||||
msgstr "Vremenska linija od {}"
|
msgstr "Vremenska linija od {}"
|
||||||
|
|
||||||
@ -234,7 +233,7 @@ msgstr "Vremenska linija omiljenih tvitova"
|
|||||||
|
|
||||||
#: ../src\controller\mainController.py:363
|
#: ../src\controller\mainController.py:363
|
||||||
#: ../src\controller\mainController.py:902
|
#: ../src\controller\mainController.py:902
|
||||||
#: ../src\controller\mainController.py:1587
|
#: ../src\controller\mainController.py:1584
|
||||||
msgid "Likes for {}"
|
msgid "Likes for {}"
|
||||||
msgstr "Sviđanja od {}"
|
msgstr "Sviđanja od {}"
|
||||||
|
|
||||||
@ -244,7 +243,7 @@ msgstr "Vremenske linije pratilaca"
|
|||||||
|
|
||||||
#: ../src\controller\mainController.py:367
|
#: ../src\controller\mainController.py:367
|
||||||
#: ../src\controller\mainController.py:921
|
#: ../src\controller\mainController.py:921
|
||||||
#: ../src\controller\mainController.py:1589
|
#: ../src\controller\mainController.py:1586
|
||||||
msgid "Followers for {}"
|
msgid "Followers for {}"
|
||||||
msgstr "Pratioci od {}"
|
msgstr "Pratioci od {}"
|
||||||
|
|
||||||
@ -254,7 +253,7 @@ msgstr "Vremenske linije praćenih korisnika"
|
|||||||
|
|
||||||
#: ../src\controller\mainController.py:371
|
#: ../src\controller\mainController.py:371
|
||||||
#: ../src\controller\mainController.py:940
|
#: ../src\controller\mainController.py:940
|
||||||
#: ../src\controller\mainController.py:1591
|
#: ../src\controller\mainController.py:1588
|
||||||
msgid "Friends for {}"
|
msgid "Friends for {}"
|
||||||
msgstr "Prijatelji od {}"
|
msgstr "Prijatelji od {}"
|
||||||
|
|
||||||
@ -279,6 +278,7 @@ msgstr "Pretraga za {}"
|
|||||||
|
|
||||||
#: ../src\controller\mainController.py:381
|
#: ../src\controller\mainController.py:381
|
||||||
#: ../src\controller\mainController.py:982
|
#: ../src\controller\mainController.py:982
|
||||||
|
#: ../src\controller\mainController.py:1590
|
||||||
msgid "Trending topics for %s"
|
msgid "Trending topics for %s"
|
||||||
msgstr "Teme u trendu za %s"
|
msgstr "Teme u trendu za %s"
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ msgstr "Dodaj nadimak za korisnika"
|
|||||||
msgid "Alias has been set correctly for {}."
|
msgid "Alias has been set correctly for {}."
|
||||||
msgstr "Nadimak za {} je uspešno podešen."
|
msgstr "Nadimak za {} je uspešno podešen."
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:829 ../src\controller\messages.py:245
|
#: ../src\controller\mainController.py:829 ../src\controller\messages.py:327
|
||||||
msgid "MMM D, YYYY. H:m"
|
msgid "MMM D, YYYY. H:m"
|
||||||
msgstr "MMM D, YYYY. H:m"
|
msgstr "MMM D, YYYY. H:m"
|
||||||
|
|
||||||
@ -410,72 +410,52 @@ msgstr "Utišavanje kanala uključeno"
|
|||||||
msgid "Buffer mute off"
|
msgid "Buffer mute off"
|
||||||
msgstr "Utišavanje kanala isključeno"
|
msgstr "Utišavanje kanala isključeno"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1545
|
#: ../src\controller\mainController.py:1542
|
||||||
msgid "Copied"
|
msgid "Copied"
|
||||||
msgstr "Kopirano"
|
msgstr "Kopirano"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1575
|
#: ../src\controller\mainController.py:1572
|
||||||
msgid "Unable to update this buffer."
|
msgid "Unable to update this buffer."
|
||||||
msgstr "Ne mogu da ažuriram ovaj kanal."
|
msgstr "Ne mogu da ažuriram ovaj kanal."
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1578
|
#: ../src\controller\mainController.py:1575
|
||||||
msgid "Updating buffer..."
|
msgid "Updating buffer..."
|
||||||
msgstr "Ažuriram kanal..."
|
msgstr "Ažuriram kanal..."
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1581
|
#: ../src\controller\mainController.py:1578
|
||||||
msgid "{0} items retrieved"
|
msgid "{0} items retrieved"
|
||||||
msgstr "{0} primljenih stavki"
|
msgstr "{0} primljenih stavki"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1598
|
#: ../src\controller\mainController.py:1597
|
||||||
#: ../src\controller\mainController.py:1618
|
#: ../src\controller\mainController.py:1617
|
||||||
msgid "Invalid buffer"
|
msgid "Invalid buffer"
|
||||||
msgstr "Nevažeći kanal"
|
msgstr "Nevažeći kanal"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1609
|
#: ../src\controller\mainController.py:1608
|
||||||
msgid "Picture {0}"
|
msgid "Picture {0}"
|
||||||
msgstr "Slika {0}"
|
msgstr "Slika {0}"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1610
|
#: ../src\controller\mainController.py:1609
|
||||||
msgid "Select the picture"
|
msgid "Select the picture"
|
||||||
msgstr "Izaberite sliku"
|
msgstr "Izaberite sliku"
|
||||||
|
|
||||||
#: ../src\controller\mainController.py:1629
|
#: ../src\controller\mainController.py:1628
|
||||||
msgid "Unable to extract text"
|
msgid "Unable to extract text"
|
||||||
msgstr "Ne mogu da izdvojim tekst."
|
msgstr "Ne mogu da izdvojim tekst."
|
||||||
|
|
||||||
#: ../src\controller\messages.py:56
|
#: ../src\controller\messages.py:49
|
||||||
msgid "Translated"
|
msgid "Translated"
|
||||||
msgstr "Prevedeno"
|
msgstr "Prevedeno"
|
||||||
|
|
||||||
#: ../src\controller\messages.py:63
|
#: ../src\controller\messages.py:56
|
||||||
msgid "There's no URL to be shortened"
|
|
||||||
msgstr "Nema veze koja bi mogla biti skraćena"
|
|
||||||
|
|
||||||
#: ../src\controller\messages.py:67 ../src\controller\messages.py:75
|
|
||||||
msgid "URL shortened"
|
|
||||||
msgstr "Veza je skraćena"
|
|
||||||
|
|
||||||
#: ../src\controller\messages.py:82
|
|
||||||
msgid "There's no URL to be expanded"
|
|
||||||
msgstr "Nema veze koja bi mogla biti proširena"
|
|
||||||
|
|
||||||
#: ../src\controller\messages.py:86 ../src\controller\messages.py:94
|
|
||||||
msgid "URL expanded"
|
|
||||||
msgstr "Veza je proširena"
|
|
||||||
|
|
||||||
#: ../src\controller\messages.py:108
|
|
||||||
msgid "%s - %s of %d characters"
|
msgid "%s - %s of %d characters"
|
||||||
msgstr "%s - %s od %d znakova"
|
msgstr "%s - %s od %d znakova"
|
||||||
|
|
||||||
#: ../src\controller\messages.py:112
|
#: ../src\controller\messages.py:354
|
||||||
msgid "%s - %s characters"
|
|
||||||
msgstr "%s - %s znakova"
|
|
||||||
|
|
||||||
#: ../src\controller\messages.py:272
|
|
||||||
msgid "View item"
|
msgid "View item"
|
||||||
msgstr "Prikaži stavku"
|
msgstr "Prikaži stavku"
|
||||||
|
|
||||||
#: ../src\controller\messages.py:301
|
#: ../src\controller\messages.py:379
|
||||||
msgid "Link copied to clipboard."
|
msgid "Link copied to clipboard."
|
||||||
msgstr "Link kopiran u privremenu memoriju."
|
msgstr "Link kopiran u privremenu memoriju."
|
||||||
|
|
||||||
@ -667,7 +647,7 @@ msgstr "Zaustavljeno"
|
|||||||
msgid "&Record"
|
msgid "&Record"
|
||||||
msgstr "&Snimi"
|
msgstr "&Snimi"
|
||||||
|
|
||||||
#: ../src\extra\AudioUploader\audioUploader.py:136 ../src\sound.py:148
|
#: ../src\extra\AudioUploader\audioUploader.py:136 ../src\sound.py:147
|
||||||
msgid "Playing..."
|
msgid "Playing..."
|
||||||
msgstr "Reprodukujem..."
|
msgstr "Reprodukujem..."
|
||||||
|
|
||||||
@ -719,6 +699,9 @@ msgid "%s seconds"
|
|||||||
msgstr "%s sekundi"
|
msgstr "%s sekundi"
|
||||||
|
|
||||||
#: ../src\extra\AudioUploader\wx_transfer_dialogs.py:15
|
#: ../src\extra\AudioUploader\wx_transfer_dialogs.py:15
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:36
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:173
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:258
|
||||||
msgid "File"
|
msgid "File"
|
||||||
msgstr "Datoteka"
|
msgstr "Datoteka"
|
||||||
|
|
||||||
@ -1486,7 +1469,7 @@ msgstr ""
|
|||||||
msgid "Send report"
|
msgid "Send report"
|
||||||
msgstr "Pošalji izveštaj"
|
msgstr "Pošalji izveštaj"
|
||||||
|
|
||||||
#: ../src\issueReporter\wx_ui.py:75 ../src\wxUI\dialogs\filterDialogs.py:84
|
#: ../src\issueReporter\wx_ui.py:75 ../src\wxUI\dialogs\filterDialogs.py:83
|
||||||
#: ../src\wxUI\dialogs\find.py:23
|
#: ../src\wxUI\dialogs\find.py:23
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Otkaži"
|
msgstr "Otkaži"
|
||||||
@ -1560,7 +1543,7 @@ msgid "New tweet"
|
|||||||
msgstr "Novi tvit"
|
msgstr "Novi tvit"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\constants.py:12 ../src\wxUI\buffers\base.py:26
|
#: ../src\keystrokeEditor\constants.py:12 ../src\wxUI\buffers\base.py:26
|
||||||
#: ../src\wxUI\commonMessageDialogs.py:10 ../src\wxUI\dialogs\message.py:126
|
#: ../src\wxUI\commonMessageDialogs.py:10
|
||||||
msgid "Retweet"
|
msgid "Retweet"
|
||||||
msgstr "Retvituj"
|
msgstr "Retvituj"
|
||||||
|
|
||||||
@ -1773,7 +1756,7 @@ msgstr "Prečica"
|
|||||||
msgid "Action"
|
msgid "Action"
|
||||||
msgstr "Radnja"
|
msgstr "Radnja"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\wx_ui.py:18 ../src\wxUI\dialogs\filterDialogs.py:131
|
#: ../src\keystrokeEditor\wx_ui.py:18 ../src\wxUI\dialogs\filterDialogs.py:135
|
||||||
#: ../src\wxUI\dialogs\lists.py:20 ../src\wxUI\dialogs\userAliasDialogs.py:53
|
#: ../src\wxUI\dialogs\lists.py:20 ../src\wxUI\dialogs\userAliasDialogs.py:53
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr "Izmeni"
|
msgstr "Izmeni"
|
||||||
@ -1823,7 +1806,7 @@ msgstr "Windows"
|
|||||||
msgid "Key"
|
msgid "Key"
|
||||||
msgstr "Taster"
|
msgstr "Taster"
|
||||||
|
|
||||||
#: ../src\keystrokeEditor\wx_ui.py:71 ../src\wxUI\dialogs\filterDialogs.py:82
|
#: ../src\keystrokeEditor\wx_ui.py:71 ../src\wxUI\dialogs\filterDialogs.py:80
|
||||||
#: ../src\wxUI\dialogs\find.py:21 ../src\wxUI\dialogs\userAliasDialogs.py:23
|
#: ../src\wxUI\dialogs\find.py:21 ../src\wxUI\dialogs\userAliasDialogs.py:23
|
||||||
#: ../src\wxUI\dialogs\utils.py:36
|
#: ../src\wxUI\dialogs\utils.py:36
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
@ -1999,16 +1982,18 @@ msgstr "Privatno"
|
|||||||
msgid "public"
|
msgid "public"
|
||||||
msgstr "Javno"
|
msgstr "Javno"
|
||||||
|
|
||||||
#: ../src\sessions\twitter\session.py:209
|
#: ../src\sessions\twitter\session.py:211
|
||||||
|
#: ../src\sessions\twitter\session.py:238
|
||||||
msgid "%s failed. Reason: %s"
|
msgid "%s failed. Reason: %s"
|
||||||
msgstr "%s nije uspelo. Razlog: %s"
|
msgstr "%s nije uspelo. Razlog: %s"
|
||||||
|
|
||||||
#: ../src\sessions\twitter\session.py:215
|
#: ../src\sessions\twitter\session.py:217
|
||||||
|
#: ../src\sessions\twitter\session.py:241
|
||||||
msgid "%s succeeded."
|
msgid "%s succeeded."
|
||||||
msgstr "%s uspelo."
|
msgstr "%s uspelo."
|
||||||
|
|
||||||
#: ../src\sessions\twitter\session.py:424
|
#: ../src\sessions\twitter\session.py:450
|
||||||
#: ../src\sessions\twitter\session.py:502
|
#: ../src\sessions\twitter\session.py:528
|
||||||
msgid "Deleted account"
|
msgid "Deleted account"
|
||||||
msgstr "Obrisan nalog"
|
msgstr "Obrisan nalog"
|
||||||
|
|
||||||
@ -2036,7 +2021,7 @@ msgstr "Autorizacija naloga..."
|
|||||||
msgid "Enter your PIN code here"
|
msgid "Enter your PIN code here"
|
||||||
msgstr "Ovde upišite vaš PIN kod"
|
msgstr "Ovde upišite vaš PIN kod"
|
||||||
|
|
||||||
#: ../src\sound.py:161
|
#: ../src\sound.py:160
|
||||||
msgid "Stopped."
|
msgid "Stopped."
|
||||||
msgstr "Zaustavljeno"
|
msgstr "Zaustavljeno"
|
||||||
|
|
||||||
@ -2088,10 +2073,6 @@ msgstr ""
|
|||||||
msgid "Client"
|
msgid "Client"
|
||||||
msgstr "Klijent"
|
msgstr "Klijent"
|
||||||
|
|
||||||
#: ../src\wxUI\buffers\base.py:12
|
|
||||||
msgid "Text"
|
|
||||||
msgstr "Tekst"
|
|
||||||
|
|
||||||
#: ../src\wxUI\buffers\base.py:12 ../src\wxUI\buffers\events.py:14
|
#: ../src\wxUI\buffers\base.py:12 ../src\wxUI\buffers\events.py:14
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Datum"
|
msgstr "Datum"
|
||||||
@ -2103,6 +2084,11 @@ msgstr "Datum"
|
|||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "Korisnik"
|
msgstr "Korisnik"
|
||||||
|
|
||||||
|
#: ../src\wxUI\buffers\base.py:12
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:48
|
||||||
|
msgid "Text"
|
||||||
|
msgstr "Tekst"
|
||||||
|
|
||||||
#: ../src\wxUI\buffers\base.py:28
|
#: ../src\wxUI\buffers\base.py:28
|
||||||
msgid "Direct message"
|
msgid "Direct message"
|
||||||
msgstr "Direktna poruka"
|
msgstr "Direktna poruka"
|
||||||
@ -2359,55 +2345,6 @@ msgstr ""
|
|||||||
"{0} je neočekivano zatvoren pri poslednjem pokretanju. Ako se problem "
|
"{0} je neočekivano zatvoren pri poslednjem pokretanju. Ako se problem "
|
||||||
"nastavi, molimo prijavite ga{0} programerima."
|
"nastavi, molimo prijavite ga{0} programerima."
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:10
|
|
||||||
msgid "Add an attachment"
|
|
||||||
msgstr "Dodaj prilog"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:13
|
|
||||||
msgid "Attachments"
|
|
||||||
msgstr "Prilozi"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:14
|
|
||||||
msgid "Title"
|
|
||||||
msgstr "Naslov"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:14
|
|
||||||
msgid "Type"
|
|
||||||
msgstr "Vrsta"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:19
|
|
||||||
msgid "Add attachments"
|
|
||||||
msgstr "Dodaj priloge"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:20
|
|
||||||
msgid "&Photo"
|
|
||||||
msgstr "Slika"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:21
|
|
||||||
msgid "Remove attachment"
|
|
||||||
msgstr "Ukloni prilog"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:37 ../src\wxUI\dialogs\message.py:116
|
|
||||||
#: ../src\wxUI\dialogs\message.py:175 ../src\wxUI\dialogs\message.py:235
|
|
||||||
#: ../src\wxUI\dialogs\update_profile.py:82
|
|
||||||
msgid "Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"
|
|
||||||
msgstr "Datoteke sa slikama"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:37 ../src\wxUI\dialogs\message.py:116
|
|
||||||
#: ../src\wxUI\dialogs\message.py:175 ../src\wxUI\dialogs\message.py:235
|
|
||||||
#: ../src\wxUI\dialogs\update_profile.py:82
|
|
||||||
msgid "Select the picture to be uploaded"
|
|
||||||
msgstr "Izaberite sliku koju želite da otpremite"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:44
|
|
||||||
msgid "please provide a description"
|
|
||||||
msgstr "Molimo vas navedite opis:"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\attach.py:44 ../src\wxUI\dialogs\lists.py:14
|
|
||||||
#: ../src\wxUI\dialogs\lists.py:70
|
|
||||||
msgid "Description"
|
|
||||||
msgstr "Opis"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:15
|
#: ../src\wxUI\dialogs\configuration.py:15
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Jezik"
|
msgstr "Jezik"
|
||||||
@ -2544,7 +2481,7 @@ msgid "Status"
|
|||||||
msgstr "Status"
|
msgstr "Status"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\configuration.py:144
|
#: ../src\wxUI\dialogs\configuration.py:144
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:126
|
#: ../src\wxUI\dialogs\filterDialogs.py:130
|
||||||
msgid "Buffer"
|
msgid "Buffer"
|
||||||
msgstr "Kanal"
|
msgstr "Kanal"
|
||||||
|
|
||||||
@ -2669,91 +2606,99 @@ msgstr "Dodaci"
|
|||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Sačuvaj"
|
msgstr "Sačuvaj"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:16
|
#: ../src\wxUI\dialogs\filterDialogs.py:13
|
||||||
msgid "Create a filter for this buffer"
|
msgid "Create a filter for this buffer"
|
||||||
msgstr "Napravi filter za ovaj kanal"
|
msgstr "Napravi filter za ovaj kanal"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:17
|
#: ../src\wxUI\dialogs\filterDialogs.py:14
|
||||||
msgid "Filter title"
|
msgid "Filter title"
|
||||||
msgstr "Naziv filtera"
|
msgstr "Naziv filtera"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:26
|
#: ../src\wxUI\dialogs\filterDialogs.py:24
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:126
|
#: ../src\wxUI\dialogs\filterDialogs.py:130
|
||||||
msgid "Filter by word"
|
msgid "Filter by word"
|
||||||
msgstr "Filtriraj na osnovu reči"
|
msgstr "Filtriraj na osnovu reči"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:27
|
#: ../src\wxUI\dialogs\filterDialogs.py:25
|
||||||
msgid "Ignore tweets wich contain the following word"
|
msgid "Ignore tweets wich contain the following word"
|
||||||
msgstr "Zanemari tvitove koji sadrže sledeću reč"
|
msgstr "Zanemari tvitove koji sadrže sledeću reč"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:28
|
#: ../src\wxUI\dialogs\filterDialogs.py:26
|
||||||
msgid "Ignore tweets without the following word"
|
msgid "Ignore tweets without the following word"
|
||||||
msgstr "Zanemari tvitove bez sledeće reči"
|
msgstr "Zanemari tvitove bez sledeće reči"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:33
|
#: ../src\wxUI\dialogs\filterDialogs.py:31
|
||||||
msgid "word"
|
msgid "word"
|
||||||
msgstr "Reč"
|
msgstr "Reč"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:38
|
#: ../src\wxUI\dialogs\filterDialogs.py:36
|
||||||
msgid "Allow retweets"
|
msgid "Allow retweets"
|
||||||
msgstr "Dozvoli retvitove"
|
msgstr "Dozvoli retvitove"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:39
|
#: ../src\wxUI\dialogs\filterDialogs.py:37
|
||||||
msgid "Allow quoted tweets"
|
msgid "Allow quoted tweets"
|
||||||
msgstr "Dozvoli citirane tvitove"
|
msgstr "Dozvoli citirane tvitove"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:40
|
#: ../src\wxUI\dialogs\filterDialogs.py:38
|
||||||
msgid "Allow replies"
|
msgid "Allow replies"
|
||||||
msgstr "Dozvoli odgovore"
|
msgstr "Dozvoli odgovore"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:48
|
#: ../src\wxUI\dialogs\filterDialogs.py:46
|
||||||
msgid "Use this term as a regular expression"
|
msgid "Use this term as a regular expression"
|
||||||
msgstr "Koristi ovaj termin kao regulara nizraz"
|
msgstr "Koristi ovaj termin kao regulara nizraz"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:50
|
#: ../src\wxUI\dialogs\filterDialogs.py:48
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:126
|
#: ../src\wxUI\dialogs\filterDialogs.py:130
|
||||||
msgid "Filter by language"
|
msgid "Filter by language"
|
||||||
msgstr "Filtriraj na osnovu jezika"
|
msgstr "Filtriraj na osnovu jezika"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:51
|
#: ../src\wxUI\dialogs\filterDialogs.py:49
|
||||||
msgid "Load tweets in the following languages"
|
msgid "Load tweets in the following languages"
|
||||||
msgstr "Učitaj tvitove na sledećim jezicima"
|
msgstr "Učitaj tvitove na sledećim jezicima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:52
|
#: ../src\wxUI\dialogs\filterDialogs.py:50
|
||||||
msgid "Ignore tweets in the following languages"
|
msgid "Ignore tweets in the following languages"
|
||||||
msgstr "Zanemari tvitove na sledećim jezicima"
|
msgstr "Zanemari tvitove na sledećim jezicima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:53
|
#: ../src\wxUI\dialogs\filterDialogs.py:51
|
||||||
msgid "Don't filter by language"
|
msgid "Don't filter by language"
|
||||||
msgstr "Ne filtriraj na osnovu jezika"
|
msgstr "Ne filtriraj na osnovu jezika"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:64
|
#: ../src\wxUI\dialogs\filterDialogs.py:62
|
||||||
msgid "Supported languages"
|
msgid "Supported languages"
|
||||||
msgstr "Podržani jezici"
|
msgstr "Podržani jezici"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:69
|
#: ../src\wxUI\dialogs\filterDialogs.py:67
|
||||||
msgid "Add selected language to filter"
|
msgid "Add selected language to filter"
|
||||||
msgstr "Dodaj izabrani jezik u filter"
|
msgstr "Dodaj izabrani jezik u filter"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:73
|
#: ../src\wxUI\dialogs\filterDialogs.py:71
|
||||||
msgid "Selected languages"
|
msgid "Selected languages"
|
||||||
msgstr "Izabrani jezici"
|
msgstr "Izabrani jezici"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:75
|
#: ../src\wxUI\dialogs\filterDialogs.py:73
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:133 ../src\wxUI\dialogs\lists.py:21
|
#: ../src\wxUI\dialogs\filterDialogs.py:137 ../src\wxUI\dialogs\lists.py:21
|
||||||
#: ../src\wxUI\dialogs\lists.py:132 ../src\wxUI\dialogs\userAliasDialogs.py:57
|
#: ../src\wxUI\dialogs\lists.py:132 ../src\wxUI\dialogs\userAliasDialogs.py:57
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Ukloni"
|
msgstr "Ukloni"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:123
|
#: ../src\wxUI\dialogs\filterDialogs.py:120
|
||||||
|
msgid "Missing filter name"
|
||||||
|
msgstr "Ime filtera nedostaje"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\filterDialogs.py:120
|
||||||
|
msgid "You must define a name for the filter before creating it."
|
||||||
|
msgstr "Morate upisati ime za filter pre nego što ga napravite."
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\filterDialogs.py:127
|
||||||
msgid "Manage filters"
|
msgid "Manage filters"
|
||||||
msgstr "Upravljanje filterima"
|
msgstr "Upravljanje filterima"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:125
|
#: ../src\wxUI\dialogs\filterDialogs.py:129
|
||||||
msgid "Filters"
|
msgid "Filters"
|
||||||
msgstr "Filteri"
|
msgstr "Filteri"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\filterDialogs.py:126
|
#: ../src\wxUI\dialogs\filterDialogs.py:130
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr "Filter"
|
msgstr "Filter"
|
||||||
|
|
||||||
@ -2785,6 +2730,14 @@ msgstr "Vlasnik"
|
|||||||
msgid "mode"
|
msgid "mode"
|
||||||
msgstr "Način"
|
msgstr "Način"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\lists.py:14 ../src\wxUI\dialogs\lists.py:70
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:38
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:127
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:175
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:260
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Opis"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\lists.py:19 ../src\wxUI\dialogs\lists.py:62
|
#: ../src\wxUI\dialogs\lists.py:19 ../src\wxUI\dialogs\lists.py:62
|
||||||
msgid "Create a new list"
|
msgid "Create a new list"
|
||||||
msgstr "Stvori novu listu"
|
msgstr "Stvori novu listu"
|
||||||
@ -2841,103 +2794,6 @@ msgstr "Izaberite listu sa koje želite da uklonite korisnika"
|
|||||||
msgid "Do you really want to delete this list?"
|
msgid "Do you really want to delete this list?"
|
||||||
msgstr "Želite li zaista da izbrišete ovu listu?"
|
msgstr "Želite li zaista da izbrišete ovu listu?"
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:73 ../src\wxUI\dialogs\message.py:254
|
|
||||||
msgid "&Long tweet"
|
|
||||||
msgstr "Dug tvit"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:74 ../src\wxUI\dialogs\message.py:133
|
|
||||||
#: ../src\wxUI\dialogs\message.py:255
|
|
||||||
msgid "&Upload image..."
|
|
||||||
msgstr "Otpremi sliku..."
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:75 ../src\wxUI\dialogs\message.py:134
|
|
||||||
#: ../src\wxUI\dialogs\message.py:194 ../src\wxUI\dialogs\message.py:256
|
|
||||||
#: ../src\wxUI\dialogs\message.py:359 ../src\wxUI\dialogs\message.py:435
|
|
||||||
msgid "Check &spelling..."
|
|
||||||
msgstr "Proveri pravopis..."
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:76 ../src\wxUI\dialogs\message.py:135
|
|
||||||
#: ../src\wxUI\dialogs\message.py:195 ../src\wxUI\dialogs\message.py:257
|
|
||||||
msgid "&Attach audio..."
|
|
||||||
msgstr "Priloži zvučni zapis..."
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:77 ../src\wxUI\dialogs\message.py:136
|
|
||||||
#: ../src\wxUI\dialogs\message.py:196 ../src\wxUI\dialogs\message.py:258
|
|
||||||
msgid "Sh&orten URL"
|
|
||||||
msgstr "Skrati vezu"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:78 ../src\wxUI\dialogs\message.py:137
|
|
||||||
#: ../src\wxUI\dialogs\message.py:197 ../src\wxUI\dialogs\message.py:259
|
|
||||||
#: ../src\wxUI\dialogs\message.py:360 ../src\wxUI\dialogs\message.py:436
|
|
||||||
msgid "&Expand URL"
|
|
||||||
msgstr "Proširi vezu"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:81 ../src\wxUI\dialogs\message.py:140
|
|
||||||
#: ../src\wxUI\dialogs\message.py:200 ../src\wxUI\dialogs\message.py:262
|
|
||||||
#: ../src\wxUI\dialogs\message.py:362 ../src\wxUI\dialogs\message.py:438
|
|
||||||
msgid "&Translate..."
|
|
||||||
msgstr "Prevedi..."
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:82 ../src\wxUI\dialogs\message.py:141
|
|
||||||
#: ../src\wxUI\dialogs\message.py:186 ../src\wxUI\dialogs\message.py:263
|
|
||||||
msgid "Auto&complete users"
|
|
||||||
msgstr "&Automatsko dovršavanje"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:83 ../src\wxUI\dialogs\message.py:142
|
|
||||||
#: ../src\wxUI\dialogs\message.py:201 ../src\wxUI\dialogs\message.py:264
|
|
||||||
msgid "Sen&d"
|
|
||||||
msgstr "Pošalji"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:85 ../src\wxUI\dialogs\message.py:144
|
|
||||||
#: ../src\wxUI\dialogs\message.py:203 ../src\wxUI\dialogs\message.py:266
|
|
||||||
#: ../src\wxUI\dialogs\message.py:363 ../src\wxUI\dialogs\message.py:439
|
|
||||||
msgid "C&lose"
|
|
||||||
msgstr "Zatvori"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:184
|
|
||||||
msgid "&Recipient"
|
|
||||||
msgstr "Primalac"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:245
|
|
||||||
msgid "&Mention to all"
|
|
||||||
msgstr "Spomeni sve"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:299
|
|
||||||
msgid "Tweet - %i characters "
|
|
||||||
msgstr "Tvit - %i znakova"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:316
|
|
||||||
msgid "Image description"
|
|
||||||
msgstr "Opis slike"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:327
|
|
||||||
msgid "Retweets: "
|
|
||||||
msgstr "Retvitova"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:332
|
|
||||||
msgid "Likes: "
|
|
||||||
msgstr "Sviđanja:"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:337
|
|
||||||
msgid "Source: "
|
|
||||||
msgstr "Izvor:"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:342 ../src\wxUI\dialogs\message.py:423
|
|
||||||
msgid "Date: "
|
|
||||||
msgstr "Datum:"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:357 ../src\wxUI\dialogs\message.py:433
|
|
||||||
msgid "Copy link to clipboard"
|
|
||||||
msgstr "Kopiraj link u privremenu memoriju"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:408
|
|
||||||
msgid "View"
|
|
||||||
msgstr "Vidi"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\message.py:410
|
|
||||||
msgid "Item"
|
|
||||||
msgstr "Stavka"
|
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\search.py:12
|
#: ../src\wxUI\dialogs\search.py:12
|
||||||
msgid "Search on Twitter"
|
msgid "Search on Twitter"
|
||||||
msgstr "Pretraži ttwitter"
|
msgstr "Pretraži ttwitter"
|
||||||
@ -3019,6 +2875,225 @@ msgstr "Gradu"
|
|||||||
msgid "&Location"
|
msgid "&Location"
|
||||||
msgstr "Mesto"
|
msgstr "Mesto"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:33
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:49
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:170
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:255
|
||||||
|
msgid "Attachments"
|
||||||
|
msgstr "Prilozi"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:37
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:174
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:259
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Vrsta"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:40
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:177
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:262
|
||||||
|
msgid "Delete attachment"
|
||||||
|
msgstr "Ukloni prilog"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:45
|
||||||
|
msgid "Added Tweets"
|
||||||
|
msgstr "Dodati tvitovi"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:52
|
||||||
|
msgid "Delete tweet"
|
||||||
|
msgstr "Obriši tvit"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:57
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:192
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:267
|
||||||
|
msgid "A&dd..."
|
||||||
|
msgstr "&Dodaj..."
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:59
|
||||||
|
msgid "Add t&weet"
|
||||||
|
msgstr "Dodaj t&vit"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:62
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:194
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:269
|
||||||
|
msgid "&Attach audio..."
|
||||||
|
msgstr "Priloži zvučni zapis..."
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:66
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:198
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:237
|
||||||
|
msgid "Auto&complete users"
|
||||||
|
msgstr "&Automatsko dovršavanje"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:68
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:200
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:273
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:367
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:440
|
||||||
|
msgid "Check &spelling..."
|
||||||
|
msgstr "Proveri pravopis..."
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:70
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:202
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:275
|
||||||
|
msgid "&Translate"
|
||||||
|
msgstr "&Prevedi"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:74
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:206
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:279
|
||||||
|
msgid "Sen&d"
|
||||||
|
msgstr "Pošalji"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:118
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:220
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:299
|
||||||
|
msgid "Image"
|
||||||
|
msgstr "Slika"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:120
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:222
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:301
|
||||||
|
msgid "Video"
|
||||||
|
msgstr "Video"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:122
|
||||||
|
msgid "Poll"
|
||||||
|
msgstr "Anketa"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:127
|
||||||
|
msgid "please provide a description"
|
||||||
|
msgstr "Molimo vas navedite opis:"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:134
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:292
|
||||||
|
#: ../src\wxUI\dialogs\update_profile.py:82
|
||||||
|
msgid "Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"
|
||||||
|
msgstr "Datoteke sa slikama"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:134
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:292
|
||||||
|
#: ../src\wxUI\dialogs\update_profile.py:82
|
||||||
|
msgid "Select the picture to be uploaded"
|
||||||
|
msgstr "Izaberite sliku koju želite da otpremite"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:141
|
||||||
|
msgid "Select the video to be uploaded"
|
||||||
|
msgstr "Izaberite video zapis koji želite da otpremite"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:141
|
||||||
|
msgid "Video files (*.mp4)|*.mp4"
|
||||||
|
msgstr "Datoteke video zapisa (*.mp4)|*.mp4"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:147
|
||||||
|
msgid "Error adding attachment"
|
||||||
|
msgstr "Greška pri dodavanju priloga"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:147
|
||||||
|
msgid ""
|
||||||
|
"It is not possible to add more attachments. Please make sure your tweet "
|
||||||
|
"complies with Twitter'S attachment rules. You can add only one video or GIF "
|
||||||
|
"in every tweet, and a maximum of 4 photos."
|
||||||
|
msgstr ""
|
||||||
|
"Nije moguće dodati više priloga. Molimo uverite se da vaš tvit poštuje "
|
||||||
|
"Twitter pravila o prilozima. Možete dodati samo jedan video ili GIF u svaki "
|
||||||
|
"tvit, i najviše 4 slike."
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:182
|
||||||
|
msgid "&Mention to all"
|
||||||
|
msgstr "Spomeni sve"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:235
|
||||||
|
msgid "&Recipient"
|
||||||
|
msgstr "Primalac"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:307
|
||||||
|
msgid "Tweet - %i characters "
|
||||||
|
msgstr "Tvit - %i znakova"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:324
|
||||||
|
msgid "Image description"
|
||||||
|
msgstr "Opis slike"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:335
|
||||||
|
msgid "Retweets: "
|
||||||
|
msgstr "Retvitova"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:340
|
||||||
|
msgid "Likes: "
|
||||||
|
msgstr "Sviđanja:"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:345
|
||||||
|
msgid "Source: "
|
||||||
|
msgstr "Izvor:"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:350
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:428
|
||||||
|
msgid "Date: "
|
||||||
|
msgstr "Datum:"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:365
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:438
|
||||||
|
msgid "Copy link to clipboard"
|
||||||
|
msgstr "Kopiraj link u privremenu memoriju"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:368
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:443
|
||||||
|
msgid "&Translate..."
|
||||||
|
msgstr "Prevedi..."
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:369
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:444
|
||||||
|
msgid "C&lose"
|
||||||
|
msgstr "Zatvori"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:413
|
||||||
|
msgid "View"
|
||||||
|
msgstr "Vidi"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:415
|
||||||
|
msgid "Item"
|
||||||
|
msgstr "Stavka"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:441
|
||||||
|
msgid "&Expand URL"
|
||||||
|
msgstr "Proširi vezu"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:480
|
||||||
|
msgid "Add a poll"
|
||||||
|
msgstr "Dodaj anketu"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:484
|
||||||
|
msgid "Participation time (in days)"
|
||||||
|
msgstr "Vreme učešća (u danima)"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:491
|
||||||
|
msgid "Choices"
|
||||||
|
msgstr "Opcije"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:495
|
||||||
|
msgid "Option 1"
|
||||||
|
msgstr "Opcija 1"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:501
|
||||||
|
msgid "Option 2"
|
||||||
|
msgstr "Opcija 2"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:507
|
||||||
|
msgid "Option 3"
|
||||||
|
msgstr "Opcija 3"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:513
|
||||||
|
msgid "Option 4"
|
||||||
|
msgstr "Opcija 4"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:540
|
||||||
|
msgid "Not enough information"
|
||||||
|
msgstr "Nema dovoljno informacija"
|
||||||
|
|
||||||
|
#: ../src\wxUI\dialogs\twitterDialogs\tweetDialogs.py:540
|
||||||
|
msgid "Please make sure you have provided at least two options for the poll."
|
||||||
|
msgstr "Molimo uverite se da ste ponudili bar dve opcije u anketi."
|
||||||
|
|
||||||
#: ../src\wxUI\dialogs\update_profile.py:10
|
#: ../src\wxUI\dialogs\update_profile.py:10
|
||||||
msgid "Update your profile"
|
msgid "Update your profile"
|
||||||
msgstr "Ažurirajte vaš profil"
|
msgstr "Ažurirajte vaš profil"
|
||||||
@ -3435,6 +3510,42 @@ msgstr "Ažuriraj"
|
|||||||
msgid "Your {0} version is up to date"
|
msgid "Your {0} version is up to date"
|
||||||
msgstr "Imate najnoviju verziju {0}."
|
msgstr "Imate najnoviju verziju {0}."
|
||||||
|
|
||||||
|
#~ msgid "Photo"
|
||||||
|
#~ msgstr "Slika"
|
||||||
|
|
||||||
|
#~ msgid "There's no URL to be shortened"
|
||||||
|
#~ msgstr "Nema veze koja bi mogla biti skraćena"
|
||||||
|
|
||||||
|
#~ msgid "URL shortened"
|
||||||
|
#~ msgstr "Veza je skraćena"
|
||||||
|
|
||||||
|
#~ msgid "There's no URL to be expanded"
|
||||||
|
#~ msgstr "Nema veze koja bi mogla biti proširena"
|
||||||
|
|
||||||
|
#~ msgid "URL expanded"
|
||||||
|
#~ msgstr "Veza je proširena"
|
||||||
|
|
||||||
|
#~ msgid "%s - %s characters"
|
||||||
|
#~ msgstr "%s - %s znakova"
|
||||||
|
|
||||||
|
#~ msgid "Title"
|
||||||
|
#~ msgstr "Naslov"
|
||||||
|
|
||||||
|
#~ msgid "Add attachments"
|
||||||
|
#~ msgstr "Dodaj priloge"
|
||||||
|
|
||||||
|
#~ msgid "&Photo"
|
||||||
|
#~ msgstr "Slika"
|
||||||
|
|
||||||
|
#~ msgid "&Long tweet"
|
||||||
|
#~ msgstr "Dug tvit"
|
||||||
|
|
||||||
|
#~ msgid "&Upload image..."
|
||||||
|
#~ msgstr "Otpremi sliku..."
|
||||||
|
|
||||||
|
#~ msgid "Sh&orten URL"
|
||||||
|
#~ msgstr "Skrati vezu"
|
||||||
|
|
||||||
#~ msgid "Friends' Timelines"
|
#~ msgid "Friends' Timelines"
|
||||||
#~ msgstr "Vremenska linija prijatelja"
|
#~ msgstr "Vremenska linija prijatelja"
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import platform
|
|||||||
system = platform.system()
|
system = platform.system()
|
||||||
from . import utils
|
from . import utils
|
||||||
import re
|
import re
|
||||||
import html.entities
|
|
||||||
import time
|
import time
|
||||||
import output
|
import output
|
||||||
import languageHandler
|
import languageHandler
|
||||||
@ -11,21 +10,9 @@ import arrow
|
|||||||
import logging
|
import logging
|
||||||
import config
|
import config
|
||||||
from .long_tweets import twishort, tweets
|
from .long_tweets import twishort, tweets
|
||||||
|
from .utils import StripChars
|
||||||
log = logging.getLogger("compose")
|
log = logging.getLogger("compose")
|
||||||
|
|
||||||
def StripChars(s):
|
|
||||||
"""Converts any html entities in s to their unicode-decoded equivalents and returns a string."""
|
|
||||||
entity_re = re.compile(r"&(#\d+|\w+);")
|
|
||||||
def matchFunc(match):
|
|
||||||
"""Nested function to handle a match object.
|
|
||||||
If we match &blah; and it's not found, &blah; will be returned.
|
|
||||||
if we match #\d+, unichr(digits) will be returned.
|
|
||||||
Else, a unicode string will be returned."""
|
|
||||||
if match.group(1).startswith('#'): return chr(int(match.group(1)[1:]))
|
|
||||||
replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1))
|
|
||||||
return replacement
|
|
||||||
return str(entity_re.sub(matchFunc, s))
|
|
||||||
|
|
||||||
chars = "abcdefghijklmnopqrstuvwxyz"
|
chars = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None):
|
def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None):
|
||||||
|
@ -36,9 +36,9 @@ class Session(base.baseSession):
|
|||||||
return self.order_direct_messages(data)
|
return self.order_direct_messages(data)
|
||||||
num = 0
|
num = 0
|
||||||
last_id = None
|
last_id = None
|
||||||
if (name in self.db) == False:
|
if self.db.get(name) == None:
|
||||||
self.db[name] = []
|
self.db[name] = []
|
||||||
if ("users" in self.db) == False:
|
if self.db.get("users") == None:
|
||||||
self.db["users"] = {}
|
self.db["users"] = {}
|
||||||
objects = self.db[name]
|
objects = self.db[name]
|
||||||
if ignore_older and len(self.db[name]) > 0:
|
if ignore_older and len(self.db[name]) > 0:
|
||||||
@ -136,7 +136,7 @@ class Session(base.baseSession):
|
|||||||
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
|
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
|
||||||
try:
|
try:
|
||||||
log.debug("Logging in to twitter...")
|
log.debug("Logging in to twitter...")
|
||||||
self.auth = tweepy.OAuthHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
self.auth = tweepy.OAuth1UserHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
||||||
self.auth.set_access_token(self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"])
|
self.auth.set_access_token(self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"])
|
||||||
self.twitter = tweepy.API(self.auth)
|
self.twitter = tweepy.API(self.auth)
|
||||||
self.twitter_v2 = tweepy.Client(consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"])
|
self.twitter_v2 = tweepy.Client(consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"])
|
||||||
@ -158,7 +158,7 @@ class Session(base.baseSession):
|
|||||||
if self.logged == True:
|
if self.logged == True:
|
||||||
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
||||||
else:
|
else:
|
||||||
self.auth = tweepy.OAuthHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
self.auth = tweepy.OAuth1UserHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
||||||
redirect_url = self.auth.get_authorization_url()
|
redirect_url = self.auth.get_authorization_url()
|
||||||
webbrowser.open_new_tab(redirect_url)
|
webbrowser.open_new_tab(redirect_url)
|
||||||
self.authorisation_dialog = authorisationDialog()
|
self.authorisation_dialog = authorisationDialog()
|
||||||
@ -204,7 +204,7 @@ class Session(base.baseSession):
|
|||||||
except TweepyException as e:
|
except TweepyException as e:
|
||||||
output.speak(str(e))
|
output.speak(str(e))
|
||||||
val = None
|
val = None
|
||||||
if type(e) != NotFound and type(e) != Forvidden:
|
if type(e) != NotFound and type(e) != Forbidden:
|
||||||
tries = tries+1
|
tries = tries+1
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
elif report_failure:
|
elif report_failure:
|
||||||
@ -218,6 +218,30 @@ class Session(base.baseSession):
|
|||||||
if _sound != None: self.sound.play(_sound)
|
if _sound != None: self.sound.play(_sound)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
def api_call_v2(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs):
|
||||||
|
finished = False
|
||||||
|
tries = 0
|
||||||
|
if preexec_message:
|
||||||
|
output.speak(preexec_message, True)
|
||||||
|
while finished==False and tries < 25:
|
||||||
|
try:
|
||||||
|
val = getattr(self.twitter_v2, call_name)(*args, **kwargs)
|
||||||
|
finished = True
|
||||||
|
except TweepyException as e:
|
||||||
|
log.exception("Error sending the tweet.")
|
||||||
|
output.speak(str(e))
|
||||||
|
val = None
|
||||||
|
if type(e) != NotFound and type(e) != Forbidden:
|
||||||
|
tries = tries+1
|
||||||
|
time.sleep(5)
|
||||||
|
elif report_failure:
|
||||||
|
output.speak(_("%s failed. Reason: %s") % (action, str(e)))
|
||||||
|
finished = True
|
||||||
|
if report_success:
|
||||||
|
output.speak(_("%s succeeded.") % action)
|
||||||
|
if _sound != None: self.sound.play(_sound)
|
||||||
|
return val
|
||||||
|
|
||||||
def search(self, name, *args, **kwargs):
|
def search(self, name, *args, **kwargs):
|
||||||
""" Search in twitter, passing args and kwargs as arguments to the Twython function."""
|
""" Search in twitter, passing args and kwargs as arguments to the Twython function."""
|
||||||
tl = self.twitter.search_tweets(*args, **kwargs)
|
tl = self.twitter.search_tweets(*args, **kwargs)
|
||||||
@ -232,20 +256,21 @@ class Session(base.baseSession):
|
|||||||
tl = self.call_paged("favorites", *args, **kwargs)
|
tl = self.call_paged("favorites", *args, **kwargs)
|
||||||
return self.order_buffer(name, tl)
|
return self.order_buffer(name, tl)
|
||||||
|
|
||||||
def call_paged(self, update_function, *args, **kwargs):
|
def call_paged(self, update_function, name, *args, **kwargs):
|
||||||
""" Makes a call to the Twitter API methods several times. Useful for get methods.
|
""" Makes a call to the Twitter API methods several times. Useful for get methods.
|
||||||
this function is needed for retrieving more than 200 items.
|
this function is needed for retrieving more than 200 items.
|
||||||
update_function str: The function to call. This function must be child of self.twitter
|
update_function str: The function to call. This function must be child of self.twitter
|
||||||
args and kwargs are passed to update_function.
|
args and kwargs are passed to update_function.
|
||||||
returns a list with all items retrieved."""
|
returns a list with all items retrieved."""
|
||||||
max = 0
|
|
||||||
results = []
|
results = []
|
||||||
data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
|
if self.db.get(name) == None or self.db.get(name) == []:
|
||||||
results.extend(data)
|
since_id = None
|
||||||
for i in range(0, max):
|
else:
|
||||||
if i == 0: max_id = results[-1].id
|
if self.settings["general"]["reverse_timelines"] == False:
|
||||||
else: max_id = results[0].id
|
since_id = self.db[name][-1].id
|
||||||
data = getattr(self.twitter, update_function)(max_id=max_id, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
|
else:
|
||||||
|
since_id = self.db[name][0].id
|
||||||
|
data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], since_id=since_id, *args, **kwargs)
|
||||||
results.extend(data)
|
results.extend(data)
|
||||||
results.reverse()
|
results.reverse()
|
||||||
return results
|
return results
|
||||||
@ -591,3 +616,54 @@ class Session(base.baseSession):
|
|||||||
return
|
return
|
||||||
if user != self.db["user_name"]:
|
if user != self.db["user_name"]:
|
||||||
log.debug("Connected streaming endpoint on account {}".format(user))
|
log.debug("Connected streaming endpoint on account {}".format(user))
|
||||||
|
|
||||||
|
def send_tweet(self, *tweets):
|
||||||
|
""" Convenience function to send a thread. """
|
||||||
|
in_reply_to_status_id = None
|
||||||
|
for obj in tweets:
|
||||||
|
# When quoting a tweet, the tweet_data dict might contain a parameter called quote_tweet_id. Let's add it, or None, so quotes will be posted successfully.
|
||||||
|
if len(obj["attachments"]) == 0:
|
||||||
|
item = self.api_call_v2(call_name="create_tweet", text=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"], quote_tweet_id=obj.get("quote_tweet_id"))
|
||||||
|
in_reply_to_status_id = item.data["id"]
|
||||||
|
else:
|
||||||
|
media_ids = []
|
||||||
|
for i in obj["attachments"]:
|
||||||
|
img = self.api_call("media_upload", filename=i["file"])
|
||||||
|
if i["type"] == "photo":
|
||||||
|
self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"])
|
||||||
|
media_ids.append(img.media_id)
|
||||||
|
item = self.api_call_v2(call_name="create_tweet", text=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, media_ids=media_ids, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"], quote_tweet_id=obj.get("quote_tweet_id"))
|
||||||
|
in_reply_to_status_id = item.data["id"]
|
||||||
|
|
||||||
|
def reply(self, text="", in_reply_to_status_id=None, attachments=[], *args, **kwargs):
|
||||||
|
if len(attachments) == 0:
|
||||||
|
item = self.api_call_v2(call_name="create_tweet", text=text, _sound="reply_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
media_ids = []
|
||||||
|
for i in attachments:
|
||||||
|
img = self.api_call("media_upload", filename=i["file"])
|
||||||
|
if i["type"] == "photo":
|
||||||
|
self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"])
|
||||||
|
media_ids.append(img.media_id)
|
||||||
|
item = self.api_call_v2(call_name="create_tweet", text=text, _sound="reply_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, media_ids=media_ids, *args, **kwargs)
|
||||||
|
|
||||||
|
def direct_message(self, text, recipient, attachment=None, *args, **kwargs):
|
||||||
|
if attachment == None:
|
||||||
|
item = self.api_call(call_name="send_direct_message", recipient_id=recipient, text=text)
|
||||||
|
else:
|
||||||
|
if attachment["type"] == "photo":
|
||||||
|
media_category = "DmImage"
|
||||||
|
elif attachment["type"] == "gif":
|
||||||
|
media_category = "DmGif"
|
||||||
|
elif attachment["type"] == "video":
|
||||||
|
media_category = "DmVideo"
|
||||||
|
media = self.api_call("media_upload", filename=attachment["file"], media_category=media_category)
|
||||||
|
item = self.api_call(call_name="send_direct_message", recipient_id=recipient, text=text, attachment_type="media", attachment_media_id=media.media_id)
|
||||||
|
if item != None:
|
||||||
|
sent_dms = self.db["sent_direct_messages"]
|
||||||
|
if self.settings["general"]["reverse_timelines"] == False:
|
||||||
|
sent_dms.append(item)
|
||||||
|
else:
|
||||||
|
sent_dms.insert(0, item)
|
||||||
|
self.db["sent_direct_messages"] = sent_dms
|
||||||
|
pub.sendMessage("sent-dm", data=item, user=self.db["user_name"])
|
||||||
|
159
src/sessions/twitter/templates.py
Normal file
159
src/sessions/twitter/templates.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import arrow
|
||||||
|
import languageHandler
|
||||||
|
from string import Template
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
# Define variables that would be available for all template objects.
|
||||||
|
# This will be used for the edit template dialog.
|
||||||
|
# Available variables for tweet objects.
|
||||||
|
tweet_variables = ["date", "display_name", "screen_name", "source", "lang", "text", "image_descriptions"]
|
||||||
|
dm_variables = ["date", "sender_display_name", "sender_screen_name", "recipient_display_name", "recipient_display_name", "text"]
|
||||||
|
person_variables = ["display_name", "screen_name", "location", "description", "followers", "following", "listed", "likes", "tweets", "created_at"]
|
||||||
|
|
||||||
|
# Default, translatable templates.
|
||||||
|
tweet_default_template = _("$display_name, $text $image_descriptions $date. $source")
|
||||||
|
dm_default_template = _("$sender_display_name, $text $date")
|
||||||
|
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
|
||||||
|
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.")
|
||||||
|
|
||||||
|
def process_date(field, relative_times=True, offset_seconds=0):
|
||||||
|
original_date = arrow.get(field, locale="en")
|
||||||
|
if relative_times == True:
|
||||||
|
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||||
|
else:
|
||||||
|
ts = original_date.shift(seconds=offset_seconds).format(_("dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
||||||
|
return ts
|
||||||
|
|
||||||
|
def process_text(tweet):
|
||||||
|
if hasattr(tweet, "full_text"):
|
||||||
|
text = tweet.full_text
|
||||||
|
elif hasattr(tweet, "text"):
|
||||||
|
text = tweet.text
|
||||||
|
# Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read.
|
||||||
|
text = utils.clean_mentions(utils.StripChars(text))
|
||||||
|
# Replace URLS for extended version of those.
|
||||||
|
if hasattr(tweet, "entities"):
|
||||||
|
text = utils.expand_urls(text, tweet.entities)
|
||||||
|
text = re.sub(r"https://twitter.com/\w+/status/\d+", "", text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def process_image_descriptions(entities):
|
||||||
|
""" Attempt to extract information for image descriptions. """
|
||||||
|
image_descriptions = []
|
||||||
|
for media in entities["media"]:
|
||||||
|
if media.get("ext_alt_text") != None:
|
||||||
|
image_descriptions.append(media.get("ext_alt_text"))
|
||||||
|
# Tweets retrieved via the Streaming API have a description field in media photos with image description available.
|
||||||
|
elif media.get("description") != None:
|
||||||
|
image_descriptions.append(media.get("description"))
|
||||||
|
|
||||||
|
idescriptions = ""
|
||||||
|
for image in image_descriptions:
|
||||||
|
idescriptions += _("Image description: {}.").format(image)
|
||||||
|
return idescriptions
|
||||||
|
|
||||||
|
def remove_unneeded_variables(template, variables):
|
||||||
|
for variable in variables:
|
||||||
|
template = re.sub("\$"+variable, "", template)
|
||||||
|
return template
|
||||||
|
|
||||||
|
def render_tweet(tweet, template, session, relative_times=False, offset_seconds=0):
|
||||||
|
""" Renders any given Tweet according to the passed template.
|
||||||
|
Available data for tweets will be stored in the following variables:
|
||||||
|
$date: Creation date.
|
||||||
|
$display_name: User profile name.
|
||||||
|
$screen_name: User screen name, this is the same name used to reference the user in Twitter.
|
||||||
|
$ source: Source client from where the current tweet was sent.
|
||||||
|
$lang: Two letter code for the automatically detected language for the tweet. This detection is performed by Twitter.
|
||||||
|
$text: Tweet text.
|
||||||
|
$image_descriptions: Information regarding image descriptions added by twitter users.
|
||||||
|
"""
|
||||||
|
global tweet_variables
|
||||||
|
available_data = dict()
|
||||||
|
created_at = process_date(tweet.created_at, relative_times, offset_seconds)
|
||||||
|
available_data.update(date=created_at)
|
||||||
|
# user.
|
||||||
|
available_data.update(display_name=session.get_user(tweet.user).name, screen_name=session.get_user(tweet.user).screen_name)
|
||||||
|
# Source client from where tweet was originated.
|
||||||
|
available_data.update(source=tweet.source)
|
||||||
|
if hasattr(tweet, "retweeted_status"):
|
||||||
|
if hasattr(tweet.retweeted_status, "quoted_status"):
|
||||||
|
text = "RT @{}: {} Quote from @{}: {}".format(session.get_user(tweet.retweeted_status.user).screen_name, process_text(tweet.retweeted_status), session.get_user(tweet.retweeted_status.quoted_status.user).screen_name, process_text(tweet.retweeted_status.quoted_status))
|
||||||
|
else:
|
||||||
|
text = "RT @{}: {}".format(session.get_user(tweet.retweeted_status.user).screen_name, process_text(tweet.retweeted_status))
|
||||||
|
elif hasattr(tweet, "quoted_status"):
|
||||||
|
text = "{} Quote from @{}: {}".format(process_text(tweet), session.get_user(tweet.quoted_status.user).screen_name, process_text(tweet.quoted_status))
|
||||||
|
else:
|
||||||
|
text = process_text(tweet)
|
||||||
|
available_data.update(lang=tweet.lang, text=text)
|
||||||
|
# process image descriptions
|
||||||
|
image_descriptions = ""
|
||||||
|
if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"):
|
||||||
|
image_descriptions = process_image_descriptions(tweet.quoted_status.extended_entities)
|
||||||
|
elif hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "quoted_status") and hasattr(tweet.retweeted_status.quoted_status, "extended_entities"):
|
||||||
|
image_descriptions = process_image_descriptions(tweet.retweeted_status.quoted_status.extended_entities)
|
||||||
|
elif hasattr(tweet, "extended_entities"):
|
||||||
|
image_descriptions = process_image_descriptions(tweet.extended_entities)
|
||||||
|
if image_descriptions != "":
|
||||||
|
available_data.update(image_descriptions=image_descriptions)
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
result = remove_unneeded_variables(result, tweet_variables)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def render_dm(dm, template, session, relative_times=False, offset_seconds=0):
|
||||||
|
""" Renders direct messages by using the provided template.
|
||||||
|
Available data will be stored in the following variables:
|
||||||
|
$date: Creation date.
|
||||||
|
$sender_display_name: User profile name for user who sent the dm.
|
||||||
|
$sender_screen_name: User screen name for user sending the dm, this is the same name used to reference the user in Twitter.
|
||||||
|
$recipient_display_name: User profile name for user who received the dm.
|
||||||
|
$recipient_screen_name: User screen name for user receiving the dm, this is the same name used to reference the user in Twitter.
|
||||||
|
$text: Text of the direct message.
|
||||||
|
"""
|
||||||
|
global dm_variables
|
||||||
|
available_data = dict()
|
||||||
|
available_data.update(text=utils.expand_urls(dm.message_create["message_data"]["text"], dm.message_create["message_data"]["entities"]))
|
||||||
|
# Let's remove the last 3 digits in the timestamp string.
|
||||||
|
# Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it.
|
||||||
|
original_date = arrow.get(int(dm.created_timestamp))
|
||||||
|
if relative_times == True:
|
||||||
|
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||||
|
else:
|
||||||
|
ts = original_date.shift(seconds=offset_seconds)
|
||||||
|
available_data.update(date=ts)
|
||||||
|
sender = session.get_user(dm.message_create["sender_id"])
|
||||||
|
recipient = session.get_user(dm.message_create["target"]["recipient_id"])
|
||||||
|
available_data.update(sender_display_name=sender.name, sender_screen_name=sender.screen_name, recipient_display_name=recipient.name, recipient_screen_name=recipient.screen_name)
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
result = remove_unneeded_variables(result, dm_variables)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Sesion object is not used in this function but we keep compatibility across all rendering functions.
|
||||||
|
def render_person(user, template, session=None, relative_times=True, offset_seconds=0):
|
||||||
|
""" Renders persons (any Twitter user) by using the provided template.
|
||||||
|
Available data will be stored in the following variables:
|
||||||
|
$display_name: The name of the user, as they’ve defined it. Not necessarily a person’s name. Typically capped at 50 characters, but subject to change.
|
||||||
|
$screen_name: The screen name, handle, or alias that this user identifies themselves with.
|
||||||
|
$location: The user-defined location for this account’s profile. Not necessarily a location, nor machine-parseable.
|
||||||
|
$description: The user-defined UTF-8 string describing their account.
|
||||||
|
$followers: The number of followers this account currently has. This value might be inaccurate.
|
||||||
|
$following: The number of users this account is following (AKA their “followings”). This value might be inaccurate.
|
||||||
|
$listed: The number of public lists that this user is a member of. This value might be inaccurate.
|
||||||
|
$likes: The number of Tweets this user has liked in the account’s lifetime. This value might be inaccurate.
|
||||||
|
$tweets: The number of Tweets (including retweets) issued by the user. This value might be inaccurate.
|
||||||
|
$created_at: The date and time that the user account was created on Twitter.
|
||||||
|
"""
|
||||||
|
global person_variables
|
||||||
|
available_data = dict(display_name=user.name, screen_name=user.screen_name, followers=user.followers_count, following=user.friends_count, likes=user.favourites_count, listed=user.listed_count, tweets=user.statuses_count)
|
||||||
|
# Nullable values.
|
||||||
|
nullables = ["location", "description"]
|
||||||
|
for nullable in nullables:
|
||||||
|
if hasattr(user, nullable) and getattr(user, nullable) != None:
|
||||||
|
available_data[nullable] = getattr(user, nullable)
|
||||||
|
created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds)
|
||||||
|
available_data.update(created_at=created_at)
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
result = remove_unneeded_variables(result, person_variables)
|
||||||
|
return result
|
@ -1,11 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import url_shortener, re
|
import re
|
||||||
|
import html.entities
|
||||||
import output
|
import output
|
||||||
import config
|
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
import sound
|
|
||||||
from tweepy.errors import TweepyException, NotFound, Forbidden
|
from tweepy.errors import TweepyException, NotFound, Forbidden
|
||||||
log = logging.getLogger("twitter.utils")
|
log = logging.getLogger("twitter.utils")
|
||||||
""" Some utilities for the twitter interface."""
|
""" Some utilities for the twitter interface."""
|
||||||
@ -18,6 +17,19 @@ url_re = re.compile(r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4
|
|||||||
url_re2 = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ \\n\\t]*")
|
url_re2 = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ \\n\\t]*")
|
||||||
bad_chars = '\'\\\n.,[](){}:;"'
|
bad_chars = '\'\\\n.,[](){}:;"'
|
||||||
|
|
||||||
|
def StripChars(s):
|
||||||
|
"""Converts any html entities in s to their unicode-decoded equivalents and returns a string."""
|
||||||
|
entity_re = re.compile(r"&(#\d+|\w+);")
|
||||||
|
def matchFunc(match):
|
||||||
|
"""Nested function to handle a match object.
|
||||||
|
If we match &blah; and it's not found, &blah; will be returned.
|
||||||
|
if we match #\d+, unichr(digits) will be returned.
|
||||||
|
Else, a unicode string will be returned."""
|
||||||
|
if match.group(1).startswith('#'): return chr(int(match.group(1)[1:]))
|
||||||
|
replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1))
|
||||||
|
return replacement
|
||||||
|
return str(entity_re.sub(matchFunc, s))
|
||||||
|
|
||||||
def find_urls_in_text(text):
|
def find_urls_in_text(text):
|
||||||
return url_re2.findall(text)
|
return url_re2.findall(text)
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ from requests import certs
|
|||||||
|
|
||||||
def get_architecture_files():
|
def get_architecture_files():
|
||||||
if platform.architecture()[0][:2] == "32":
|
if platform.architecture()[0][:2] == "32":
|
||||||
return ["../windows-dependencies/x86/oggenc2.exe", "../windows-dependencies/x86/bootstrap.exe", "../windows-dependencies/x86/libvlc.dll", "../windows-dependencies/x86/libvlccore.dll", "../windows-dependencies/x86/plugins", ["../windows-dependencies/dictionaries", "lib/enchant/data/mingw32/share/enchant/hunspell"], ["../windows-dependencies/x86/Microsoft.VC142.CRT", "."], ["../windows-dependencies/x86/Microsoft.VC142.MFC", "."]]
|
return ["../windows-dependencies/x86/oggenc2.exe", "../windows-dependencies/x86/bootstrap.exe", "../windows-dependencies/x86/libvlc.dll", "../windows-dependencies/x86/libvlccore.dll", "../windows-dependencies/x86/plugins", ["../windows-dependencies/dictionaries", "lib/enchant/data/mingw32/share/enchant/hunspell"], ["../windows-dependencies/x86/Microsoft.VC142.CRT", "."], ["../windows-dependencies/x86/Microsoft.VC142.MFC", "."], ["../windows-dependencies/x86/Microsoft.VC142.MFCLOC", "."], ["../windows-dependencies/x86/ucrt", "."]]
|
||||||
elif platform.architecture()[0][:2] == "64":
|
elif platform.architecture()[0][:2] == "64":
|
||||||
return ["../windows-dependencies/x64/oggenc2.exe", "../windows-dependencies/x64/bootstrap.exe", "../windows-dependencies/x64/libvlc.dll", "../windows-dependencies/x64/libvlccore.dll", "../windows-dependencies/x64/plugins", ["../windows-dependencies/dictionaries", "lib/enchant/data/mingw64/share/enchant/hunspell"], ["../windows-dependencies/x64/Microsoft.VC142.CRT", "."], ["../windows-dependencies/x64/Microsoft.VC142.MFC", "."]]
|
return ["../windows-dependencies/x64/oggenc2.exe", "../windows-dependencies/x64/bootstrap.exe", "../windows-dependencies/x64/libvlc.dll", "../windows-dependencies/x64/libvlccore.dll", "../windows-dependencies/x64/plugins", ["../windows-dependencies/dictionaries", "lib/enchant/data/mingw64/share/enchant/hunspell"], ["../windows-dependencies/x64/Microsoft.VC142.CRT", "."], ["../windows-dependencies/x64/Microsoft.VC142.MFC", "."], ["../windows-dependencies/x64/Microsoft.VC142.MFCLOC", "."], ["../windows-dependencies/x64/ucrt", "."]]
|
||||||
|
|
||||||
def find_sound_lib_datafiles():
|
def find_sound_lib_datafiles():
|
||||||
import os
|
import os
|
||||||
|
@ -6,7 +6,6 @@ import subprocess
|
|||||||
import platform
|
import platform
|
||||||
import tempfile
|
import tempfile
|
||||||
import glob
|
import glob
|
||||||
import url_shortener
|
|
||||||
import audio_services
|
import audio_services
|
||||||
import paths
|
import paths
|
||||||
import sound_lib
|
import sound_lib
|
||||||
@ -121,7 +120,7 @@ class URLStream(object):
|
|||||||
""" Takes an URL and prepares it to be streamed. This function will try to unshorten the passed URL and, if needed, to transform it into a valid URL."""
|
""" Takes an URL and prepares it to be streamed. This function will try to unshorten the passed URL and, if needed, to transform it into a valid URL."""
|
||||||
log.debug("Preparing URL: %s" % (url,))
|
log.debug("Preparing URL: %s" % (url,))
|
||||||
self.prepared = False
|
self.prepared = False
|
||||||
self.url = url_shortener.unshorten(url)
|
self.url = url
|
||||||
if self.url == None:
|
if self.url == None:
|
||||||
self.url = url
|
self.url = url
|
||||||
log.debug("Expanded URL: %s" % (self.url,))
|
log.debug("Expanded URL: %s" % (self.url,))
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from . import shorteners
|
|
||||||
from . __main__ import *
|
|
@ -1,46 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from functools import wraps
|
|
||||||
from . import shorteners
|
|
||||||
|
|
||||||
|
|
||||||
def service_selecter (func):
|
|
||||||
@wraps(func)
|
|
||||||
def wrapper (*args, **kwargs):
|
|
||||||
tmp = dict(kwargs)
|
|
||||||
if 'service' in tmp:
|
|
||||||
del(tmp['service'])
|
|
||||||
kwargs['service'] = find_service(kwargs['service'], **tmp) or default_service()
|
|
||||||
else:
|
|
||||||
kwargs['service'] = default_service()
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
@service_selecter
|
|
||||||
def shorten (url, service=None, **kwargs):
|
|
||||||
return service(**kwargs).shorten(url)
|
|
||||||
|
|
||||||
|
|
||||||
@service_selecter
|
|
||||||
def unshorten (url, service=None, **kwargs):
|
|
||||||
return service(**kwargs).unshorten(url)
|
|
||||||
|
|
||||||
|
|
||||||
def default_service ():
|
|
||||||
return shorteners.AcortameShortener
|
|
||||||
|
|
||||||
def find_service (service, **kwargs):
|
|
||||||
for i in shorteners.__all__:
|
|
||||||
obj = getattr(shorteners, i)(**kwargs)
|
|
||||||
if obj.name.lower() == service.lower():
|
|
||||||
return getattr(shorteners, i)
|
|
||||||
|
|
||||||
def list_services ():
|
|
||||||
return [getattr(shorteners, i)().name for i in shorteners.__all__]
|
|
||||||
|
|
||||||
def unshorten_any (url):
|
|
||||||
"""Unshortens an URL using any available unshortener. Check to see if unshortened URL was created by a shortener (nested) and unshorten if so."""
|
|
||||||
unshortened_url = shorteners.URLShortener().unshorten(url)
|
|
||||||
# None is returned if URL not unshortened
|
|
||||||
if unshortened_url:
|
|
||||||
return unshorten_any(unshortened_url)
|
|
||||||
return url
|
|
@ -1,11 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from .url_shortener import URLShortener
|
|
||||||
from .hkcim import HKCShortener
|
|
||||||
from . isgd import IsgdShortener
|
|
||||||
from . onjme import OnjmeShortener
|
|
||||||
from . tinyarrows import TinyArrowsShortener
|
|
||||||
from . tinyurl import TinyurlShortener
|
|
||||||
from . xedcc import XedccShortener
|
|
||||||
from . clckru import ClckruShortener
|
|
||||||
from . acortame import AcortameShortener
|
|
||||||
__all__ = ["HKCShortener", "IsgdShortener", "OnjmeShortener", "TinyArrowsShortener", "TinyurlShortener", "XedccShortener", "ClckruShortener", "AcortameShortener"]
|
|
@ -1,30 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
import requests
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
class AcortameShortener (URLShortener):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.name = "acorta.me"
|
|
||||||
super(AcortameShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("https://acorta.me/api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'acorta.me' in url
|
|
||||||
|
|
||||||
def unshorten (self, url):
|
|
||||||
if not 'acorta.me' in url:
|
|
||||||
#use generic expand method
|
|
||||||
return super(AcortameShortener, self).unshorten(url)
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("https://acorta.me/api.php?action=expand&format=simple&shorturl=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
@ -1,22 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import requests
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
|
|
||||||
|
|
||||||
class ClckruShortener (URLShortener):
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
self.name = "clck.ru"
|
|
||||||
super(ClckruShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("http://clck.ru/--?url=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'clck.ru' in url
|
|
@ -1,21 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import requests
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
|
|
||||||
class HKCShortener (URLShortener):
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
self.name = "HKC.im"
|
|
||||||
super(HKCShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("http://hkc.im/yourls-api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'hkc.im' in url.lower()
|
|
@ -1,22 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import requests
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
|
|
||||||
|
|
||||||
class IsgdShortener (URLShortener):
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
self.name = "Is.gd"
|
|
||||||
super(IsgdShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("http://is.gd/api.php?longurl=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'is.gd' in url
|
|
@ -1,21 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import requests
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
|
|
||||||
class OnjmeShortener (URLShortener):
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
self.name = "Onj.me"
|
|
||||||
super(OnjmeShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("http://onj.me/yourls-api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'onj.me' in url.lower()
|
|
@ -1,21 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import requests
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
|
|
||||||
class TinyArrowsShortener (URLShortener):
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
self.name = "TinyArro.ws"
|
|
||||||
super(TinyArrowsShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get("http://tinyarro.ws/api-create.php?utfpure=1&url=%s" % urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer.decode('UTF-8')
|
|
||||||
|
|
||||||
def created_url(self, url):
|
|
||||||
return "tinyarro.ws" in url
|
|
@ -1,20 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
from .url_shortener import URLShortener
|
|
||||||
import requests
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
class TinyurlShortener (URLShortener):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.name = "TinyURL.com"
|
|
||||||
super(TinyurlShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("http://tinyurl.com/api-create.php?url=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'tinyurl.com' in url
|
|
@ -1,44 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from builtins import object
|
|
||||||
import requests
|
|
||||||
|
|
||||||
class URLShortener (object):
|
|
||||||
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
#Stub out arguments, silly object. :(
|
|
||||||
return super(URLShortener, self).__init__()
|
|
||||||
|
|
||||||
def shorten (self, url):
|
|
||||||
if self.created_url(url):
|
|
||||||
return url
|
|
||||||
else:
|
|
||||||
return self._shorten(url)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
"""Returns a boolean indicating whether or not this shortener created a provided url"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def unshorten(self, url):
|
|
||||||
try:
|
|
||||||
r=requests.head(url)
|
|
||||||
if 'location' in list(r.headers.keys()):
|
|
||||||
if 'dropbox.com' in r.headers['location']:
|
|
||||||
return handle_dropbox(r.headers['location'])
|
|
||||||
else:
|
|
||||||
return r.headers['location']
|
|
||||||
else: # if the head method does not work, use get instead. Performance may decrease
|
|
||||||
r=requests.get(url, allow_redirects=False, stream=True)
|
|
||||||
# release the connection without downloading the content, we only need the response headers
|
|
||||||
r.close()
|
|
||||||
return r.headers['location']
|
|
||||||
except:
|
|
||||||
return url #we cannot expand
|
|
||||||
|
|
||||||
def handle_dropbox(url):
|
|
||||||
if url.endswith("dl=1"):
|
|
||||||
return url
|
|
||||||
else:
|
|
||||||
return url.replace("dl=0", "dl=1")
|
|
@ -1,21 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
import urllib.request, urllib.parse, urllib.error
|
|
||||||
import requests
|
|
||||||
from . url_shortener import URLShortener
|
|
||||||
|
|
||||||
class XedccShortener (URLShortener):
|
|
||||||
def __init__ (self, *args, **kwargs):
|
|
||||||
self.name = "Xed.cc"
|
|
||||||
super(XedccShortener, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _shorten (self, url):
|
|
||||||
answer = url
|
|
||||||
api = requests.get ("http://xed.cc/yourls-api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url))
|
|
||||||
if api.status_code == 200:
|
|
||||||
answer = api.text
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def created_url (self, url):
|
|
||||||
return 'xed.cc' in url.lower()
|
|
@ -28,11 +28,3 @@ file2 = open("..\\scripts\\twblue.nsi", "w", encoding="utf-8")
|
|||||||
file2.write(contents)
|
file2.write(contents)
|
||||||
file2.close()
|
file2.close()
|
||||||
print("done")
|
print("done")
|
||||||
print("Writing keys to module...")
|
|
||||||
file3 = open("appkeys.py", "w")
|
|
||||||
keys = """twitter_api_key = "{}"
|
|
||||||
twitter_api_secret = "{}"
|
|
||||||
""".format(os.environ.get("TWITTER_API_KEY"), os.environ.get("TWITTER_API_SECRET"))
|
|
||||||
file3.write(keys)
|
|
||||||
file3.close()
|
|
||||||
print("Wrote set of keys for consumer of {}".format(os.environ.get("TWITTER_API_KEY")))
|
|
@ -32,3 +32,5 @@ class trendsPanel(wx.Panel):
|
|||||||
else:
|
else:
|
||||||
self.list.select_item(0)
|
self.list.select_item(0)
|
||||||
|
|
||||||
|
def set_focus_in_list(self):
|
||||||
|
self.list.list.SetFocus()
|
||||||
|
@ -91,5 +91,9 @@ def existing_filter():
|
|||||||
def common_error(reason):
|
def common_error(reason):
|
||||||
return wx.MessageDialog(None, reason, _(u"Error"), wx.OK).ShowModal()
|
return wx.MessageDialog(None, reason, _(u"Error"), wx.OK).ShowModal()
|
||||||
|
|
||||||
|
def invalid_configuration():
|
||||||
|
return wx.MessageDialog(None, _("The configuration file is invalid."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
def dead_pid():
|
def dead_pid():
|
||||||
return wx.MessageDialog(None, _(u"{0} quit unexpectedly the last time it was run. If the problem persists, please report it to the {0} developers.").format(application.name), _(u"Warning"), wx.OK).ShowModal()
|
return wx.MessageDialog(None, _(u"{0} quit unexpectedly the last time it was run. If the problem persists, please report it to the {0} developers.").format(application.name), _(u"Warning"), wx.OK).ShowModal()
|
||||||
|
|
||||||
|
@ -1,3 +1 @@
|
|||||||
from __future__ import absolute_import
|
from . import baseDialog, trends, configuration, lists, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs, userAliasDialogs
|
||||||
from __future__ import unicode_literals
|
|
||||||
from . import baseDialog, trends, configuration, lists, message, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs, userAliasDialogs
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
""" Attach dialog. Taken from socializer: https://github.com/manuelcortez/socializer"""
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
from multiplatform_widgets import widgets
|
|
||||||
|
|
||||||
class attachDialog(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(attachDialog, self).__init__(None, title=_(u"Add an attachment"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
lbl1 = wx.StaticText(panel, wx.ID_ANY, _(u"Attachments"))
|
|
||||||
self.attachments = widgets.list(panel, _(u"Type"), _(u"Title"), style=wx.LC_REPORT)
|
|
||||||
box = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
box.Add(lbl1, 0, wx.ALL, 5)
|
|
||||||
box.Add(self.attachments.list, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(box, 0, wx.ALL, 5)
|
|
||||||
static = wx.StaticBox(panel, label=_(u"Add attachments"))
|
|
||||||
self.photo = wx.Button(panel, wx.ID_ANY, _(u"&Photo"))
|
|
||||||
self.remove = wx.Button(panel, wx.ID_ANY, _(u"Remove attachment"))
|
|
||||||
self.remove.Enable(False)
|
|
||||||
btnsizer = wx.StaticBoxSizer(static, wx.HORIZONTAL)
|
|
||||||
btnsizer.Add(self.photo, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK)
|
|
||||||
ok.SetDefault()
|
|
||||||
cancelBtn = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
btnSizer = wx.BoxSizer()
|
|
||||||
btnSizer.Add(ok, 0, wx.ALL, 5)
|
|
||||||
btnSizer.Add(cancelBtn, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return (None, None)
|
|
||||||
dsc = self.ask_description()
|
|
||||||
return (openFileDialog.GetPath(), dsc)
|
|
||||||
|
|
||||||
def ask_description(self):
|
|
||||||
dlg = wx.TextEntryDialog(self, _(u"please provide a description"), _(u"Description"))
|
|
||||||
dlg.ShowModal()
|
|
||||||
result = dlg.GetValue()
|
|
||||||
dlg.Destroy()
|
|
||||||
return result
|
|
@ -233,6 +233,20 @@ class other_buffers(wx.Panel):
|
|||||||
buffers_list.append(self.buffers.get_text_column(i, 0))
|
buffers_list.append(self.buffers.get_text_column(i, 0))
|
||||||
return buffers_list
|
return buffers_list
|
||||||
|
|
||||||
|
class templates(wx.Panel, baseDialog.BaseWXDialog):
|
||||||
|
def __init__(self, parent, tweet_template, dm_template, sent_dm_template, person_template):
|
||||||
|
super(templates, self).__init__(parent)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.tweet = wx.Button(self, wx.ID_ANY, _("Edit template for tweets. Current template: {}").format(tweet_template))
|
||||||
|
sizer.Add(self.tweet, 0, wx.ALL, 5)
|
||||||
|
self.dm = wx.Button(self, wx.ID_ANY, _("Edit template for direct messages. Current template: {}").format(dm_template))
|
||||||
|
sizer.Add(self.dm, 0, wx.ALL, 5)
|
||||||
|
self.sent_dm = wx.Button(self, wx.ID_ANY, _("Edit template for sent direct messages. Current template: {}").format(sent_dm_template))
|
||||||
|
sizer.Add(self.sent_dm, 0, wx.ALL, 5)
|
||||||
|
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for persons. Current template: {}").format(person_template))
|
||||||
|
sizer.Add(self.person, 0, wx.ALL, 5)
|
||||||
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
class ignoredClients(wx.Panel):
|
class ignoredClients(wx.Panel):
|
||||||
def __init__(self, parent, choices):
|
def __init__(self, parent, choices):
|
||||||
super(ignoredClients, self).__init__(parent=parent)
|
super(ignoredClients, self).__init__(parent=parent)
|
||||||
@ -380,6 +394,10 @@ class configurationDialog(baseDialog.BaseWXDialog):
|
|||||||
self.ignored_clients = ignoredClients(self.notebook, ignored_clients_list)
|
self.ignored_clients = ignoredClients(self.notebook, ignored_clients_list)
|
||||||
self.notebook.AddPage(self.ignored_clients, _(u"Ignored clients"))
|
self.notebook.AddPage(self.ignored_clients, _(u"Ignored clients"))
|
||||||
|
|
||||||
|
def create_templates(self, tweet_template, dm_template, sent_dm_template, person_template):
|
||||||
|
self.templates = templates(self.notebook, tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template)
|
||||||
|
self.notebook.AddPage(self.templates, _("Templates"))
|
||||||
|
|
||||||
def create_sound(self, output_devices, input_devices, soundpacks):
|
def create_sound(self, output_devices, input_devices, soundpacks):
|
||||||
self.sound = sound(self.notebook, output_devices, input_devices, soundpacks)
|
self.sound = sound(self.notebook, output_devices, input_devices, soundpacks)
|
||||||
self.notebook.AddPage(self.sound, _(u"Sound"))
|
self.notebook.AddPage(self.sound, _(u"Sound"))
|
||||||
|
@ -1,471 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
|
|
||||||
class textLimited(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(textLimited, self).__init__(parent=None, *args, **kwargs)
|
|
||||||
|
|
||||||
def createTextArea(self, message="", text=""):
|
|
||||||
if not hasattr(self, "panel"):
|
|
||||||
self.panel = wx.Panel(self)
|
|
||||||
self.label = wx.StaticText(self.panel, -1, message)
|
|
||||||
self.SetTitle(str(len(text)))
|
|
||||||
self.text = wx.TextCtrl(self.panel, -1, text, size=(439, -1),style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
|
||||||
# font = self.text.GetFont()
|
|
||||||
# dc = wx.WindowDC(self.text)
|
|
||||||
# dc.SetFont(font)
|
|
||||||
# x, y = dc.GetTextExtent("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
|
||||||
# self.text.SetSize((x, y))
|
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
|
||||||
self.text.SetFocus()
|
|
||||||
self.textBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.textBox.Add(self.label, 0, wx.ALL, 5)
|
|
||||||
self.textBox.Add(self.text, 0, wx.ALL, 5)
|
|
||||||
|
|
||||||
def text_focus(self):
|
|
||||||
self.text.SetFocus()
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
return self.text.GetValue()
|
|
||||||
|
|
||||||
def set_text(self, text):
|
|
||||||
return self.text.ChangeValue(text)
|
|
||||||
|
|
||||||
def set_title(self, new_title):
|
|
||||||
return self.SetTitle(new_title)
|
|
||||||
|
|
||||||
def enable_button(self, buttonName):
|
|
||||||
if hasattr(self, buttonName):
|
|
||||||
return getattr(self, buttonName).Enable()
|
|
||||||
|
|
||||||
def disable_button(self, buttonName):
|
|
||||||
if hasattr(self, buttonName):
|
|
||||||
return getattr(self, buttonName).Disable()
|
|
||||||
|
|
||||||
def onSelect(self, ev):
|
|
||||||
self.text.SelectAll()
|
|
||||||
|
|
||||||
def handle_keys(self, event):
|
|
||||||
shift=event.ShiftDown()
|
|
||||||
if event.GetKeyCode() == wx.WXK_RETURN and shift==False and hasattr(self,'okButton'):
|
|
||||||
wx.PostEvent(self.okButton.GetEventHandler(), wx.PyCommandEvent(wx.EVT_BUTTON.typeId,wx.ID_OK))
|
|
||||||
else:
|
|
||||||
event.Skip()
|
|
||||||
|
|
||||||
def set_cursor_at_end(self):
|
|
||||||
self.text.SetInsertionPoint(len(self.text.GetValue()))
|
|
||||||
|
|
||||||
def set_cursor_at_position(self, position):
|
|
||||||
self.text.SetInsertionPoint(position)
|
|
||||||
|
|
||||||
def get_position(self):
|
|
||||||
return self.text.GetInsertionPoint()
|
|
||||||
|
|
||||||
def popup_menu(self, menu):
|
|
||||||
self.PopupMenu(menu, self.text.GetPosition())
|
|
||||||
|
|
||||||
class tweet(textLimited):
|
|
||||||
def createControls(self, title, message, text):
|
|
||||||
self.mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.createTextArea(message, text)
|
|
||||||
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
|
|
||||||
self.long_tweet = wx.CheckBox(self.panel, -1, _(u"&Long tweet"))
|
|
||||||
self.upload_image = wx.Button(self.panel, -1, _(u"&Upload image..."), size=wx.DefaultSize)
|
|
||||||
self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize)
|
|
||||||
self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.shortenButton.Disable()
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users"))
|
|
||||||
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize)
|
|
||||||
self.okButton.SetDefault()
|
|
||||||
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.ok_cancelSizer.Add(self.autocompletionButton, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.ok_cancelSizer)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
self.panel.SetSizer(self.mainBox)
|
|
||||||
|
|
||||||
def __init__(self, title, message, text, *args, **kwargs):
|
|
||||||
super(tweet, self).__init__()
|
|
||||||
self.shift=False
|
|
||||||
self.createControls(message, title, text)
|
|
||||||
self.SetClientSize(self.mainBox.CalcMin())
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return None
|
|
||||||
return open(openFileDialog.GetPath(), "rb")
|
|
||||||
|
|
||||||
|
|
||||||
class retweet(tweet):
|
|
||||||
def createControls(self, title, message, text):
|
|
||||||
self.mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.createTextArea(message, "")
|
|
||||||
label = wx.StaticText(self.panel, -1, _(u"Retweet"))
|
|
||||||
self.text2 = wx.TextCtrl(self.panel, -1, text, size=(439, -1), style=wx.TE_MULTILINE|wx.TE_READONLY)
|
|
||||||
self.retweetBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.retweetBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
self.retweetBox.Add(self.text2, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.retweetBox, 0, wx.ALL, 5)
|
|
||||||
self.upload_image = wx.Button(self.panel, -1, _(u"&Upload image..."), size=wx.DefaultSize)
|
|
||||||
self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize)
|
|
||||||
self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.shortenButton.Disable()
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users"))
|
|
||||||
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize)
|
|
||||||
self.okButton.SetDefault()
|
|
||||||
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.ok_cancelSizer.Add(self.autocompletionButton, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.ok_cancelSizer)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
self.panel.SetSizer(self.mainBox)
|
|
||||||
|
|
||||||
def __init__(self, title, message, text, *args, **kwargs):
|
|
||||||
super(tweet, self).__init__()
|
|
||||||
self.createControls(message, title, text)
|
|
||||||
# self.onTimer(wx.EVT_CHAR_HOOK)
|
|
||||||
self.SetClientSize(self.mainBox.CalcMin())
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return None
|
|
||||||
return open(openFileDialog.GetPath(), "rb")
|
|
||||||
|
|
||||||
class dm(textLimited):
|
|
||||||
def createControls(self, title, message, users):
|
|
||||||
self.panel = wx.Panel(self)
|
|
||||||
self.mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
label = wx.StaticText(self.panel, -1, _(u"&Recipient"))
|
|
||||||
self.cb = wx.ComboBox(self.panel, -1, choices=users, value=users[0], size=wx.DefaultSize)
|
|
||||||
self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users"))
|
|
||||||
self.createTextArea(message, text="")
|
|
||||||
userBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
userBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
userBox.Add(self.cb, 0, wx.ALL, 5)
|
|
||||||
userBox.Add(self.autocompletionButton, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(userBox, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
|
|
||||||
self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize)
|
|
||||||
self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.shortenButton.Disable()
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize)
|
|
||||||
self.okButton.SetDefault()
|
|
||||||
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
self.buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
|
||||||
self.buttonsBox.Add(self.attach, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.buttonsBox, 0, wx.ALL, 5)
|
|
||||||
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox1.Add(self.shortenButton, 0, wx.ALL, 5)
|
|
||||||
self.buttonsBox1.Add(self.unshortenButton, 0, wx.ALL, 5)
|
|
||||||
self.buttonsBox1.Add(self.translateButton, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 5)
|
|
||||||
self.buttonsBox3 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox3.Add(self.okButton, 0, wx.ALL, 5)
|
|
||||||
self.buttonsBox3.Add(cancelButton, 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.buttonsBox3, 0, wx.ALL, 5)
|
|
||||||
self.panel.SetSizer(self.mainBox)
|
|
||||||
self.SetClientSize(self.mainBox.CalcMin())
|
|
||||||
|
|
||||||
def __init__(self, title, message, users, *args, **kwargs):
|
|
||||||
super(dm, self).__init__()
|
|
||||||
self.createControls(title, message, users)
|
|
||||||
# self.onTimer(wx.EVT_CHAR_HOOK)
|
|
||||||
# self.SetClientSize(self.mainBox.CalcMin())
|
|
||||||
|
|
||||||
def get_user(self):
|
|
||||||
return self.cb.GetValue()
|
|
||||||
|
|
||||||
def set_user(self, user):
|
|
||||||
return self.cb.SetValue(user)
|
|
||||||
|
|
||||||
class reply(textLimited):
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
||||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
|
||||||
return None
|
|
||||||
return open(openFileDialog.GetPath(), "rb")
|
|
||||||
|
|
||||||
def createControls(self, title, message, text):
|
|
||||||
self.mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.createTextArea(message, text)
|
|
||||||
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
|
|
||||||
self.usersbox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.mentionAll = wx.CheckBox(self.panel, -1, _(u"&Mention to all"), size=wx.DefaultSize)
|
|
||||||
self.mentionAll.Disable()
|
|
||||||
self.usersbox.Add(self.mentionAll, 0, wx.ALL, 5)
|
|
||||||
self.checkboxes = []
|
|
||||||
for i in self.users:
|
|
||||||
user_checkbox = wx.CheckBox(self.panel, -1, "@"+i, size=wx.DefaultSize)
|
|
||||||
self.checkboxes.append(user_checkbox)
|
|
||||||
self.usersbox.Add(self.checkboxes[-1], 0, wx.ALL, 5)
|
|
||||||
self.mainBox.Add(self.usersbox, 0, wx.ALL, 10)
|
|
||||||
self.long_tweet = wx.CheckBox(self.panel, -1, _(u"&Long tweet"))
|
|
||||||
self.upload_image = wx.Button(self.panel, -1, _(u"&Upload image..."), size=wx.DefaultSize)
|
|
||||||
self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize)
|
|
||||||
self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.shortenButton.Disable()
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users"))
|
|
||||||
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize)
|
|
||||||
self.okButton.SetDefault()
|
|
||||||
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 10)
|
|
||||||
self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
self.ok_cancelSizer.Add(self.autocompletionButton, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10)
|
|
||||||
self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10)
|
|
||||||
self.mainBox.Add(self.ok_cancelSizer, 0, wx.ALL, 10)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
self.panel.SetSizer(self.mainBox)
|
|
||||||
|
|
||||||
def __init__(self, title, message, text, users=[], *args, **kwargs):
|
|
||||||
self.users = users
|
|
||||||
super(reply, self).__init__()
|
|
||||||
self.shift=False
|
|
||||||
self.createControls(message, title, text)
|
|
||||||
self.SetClientSize(self.mainBox.CalcMin())
|
|
||||||
|
|
||||||
class viewTweet(widgetUtils.BaseDialog):
|
|
||||||
def set_title(self, lenght):
|
|
||||||
self.SetTitle(_(u"Tweet - %i characters ") % (lenght,))
|
|
||||||
|
|
||||||
def __init__(self, text, rt_count, favs_count, source, date="", *args, **kwargs):
|
|
||||||
super(viewTweet, self).__init__(None, size=(850,850))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Tweet"))
|
|
||||||
self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
|
||||||
dc = wx.WindowDC(self.text)
|
|
||||||
dc.SetFont(self.text.GetFont())
|
|
||||||
(x, y) = dc.GetMultiLineTextExtent("0"*140)
|
|
||||||
self.text.SetSize((x, y))
|
|
||||||
self.text.SetFocus()
|
|
||||||
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
textBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
mainBox.Add(textBox, 0, wx.ALL, 5)
|
|
||||||
label2 = wx.StaticText(panel, -1, _(u"Image description"))
|
|
||||||
self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
|
||||||
dc = wx.WindowDC(self.image_description)
|
|
||||||
dc.SetFont(self.image_description.GetFont())
|
|
||||||
(x, y) = dc.GetMultiLineTextExtent("0"*450)
|
|
||||||
self.image_description.SetSize((x, y))
|
|
||||||
self.image_description.Enable(False)
|
|
||||||
iBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
iBox.Add(label2, 0, wx.ALL, 5)
|
|
||||||
iBox.Add(self.image_description, 1, wx.EXPAND, 5)
|
|
||||||
mainBox.Add(iBox, 0, wx.ALL, 5)
|
|
||||||
rtCountLabel = wx.StaticText(panel, -1, _(u"Retweets: "))
|
|
||||||
rtCount = wx.TextCtrl(panel, -1, rt_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
rtBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
rtBox.Add(rtCountLabel, 0, wx.ALL, 5)
|
|
||||||
rtBox.Add(rtCount, 0, wx.ALL, 5)
|
|
||||||
favsCountLabel = wx.StaticText(panel, -1, _(u"Likes: "))
|
|
||||||
favsCount = wx.TextCtrl(panel, -1, favs_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
favsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
favsBox.Add(favsCountLabel, 0, wx.ALL, 5)
|
|
||||||
favsBox.Add(favsCount, 0, wx.ALL, 5)
|
|
||||||
sourceLabel = wx.StaticText(panel, -1, _(u"Source: "))
|
|
||||||
sourceTweet = wx.TextCtrl(panel, -1, source, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
sourceBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sourceBox.Add(sourceLabel, 0, wx.ALL, 5)
|
|
||||||
sourceBox.Add(sourceTweet, 0, wx.ALL, 5)
|
|
||||||
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
|
||||||
dateTweet = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
dc = wx.WindowDC(dateTweet)
|
|
||||||
dc.SetFont(dateTweet.GetFont())
|
|
||||||
(x, y) = dc.GetTextExtent("0"*100)
|
|
||||||
dateTweet.SetSize((x, y))
|
|
||||||
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
|
||||||
dateBox.Add(dateTweet, 0, wx.ALL, 5)
|
|
||||||
infoBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
infoBox.Add(rtBox, 0, wx.ALL, 5)
|
|
||||||
infoBox.Add(favsBox, 0, wx.ALL, 5)
|
|
||||||
infoBox.Add(sourceBox, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(infoBox, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
|
||||||
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
|
||||||
self.share.Enable(False)
|
|
||||||
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
cancelButton.SetDefault()
|
|
||||||
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.SetClientSize(mainBox.CalcMin())
|
|
||||||
|
|
||||||
def set_text(self, text):
|
|
||||||
self.text.ChangeValue(text)
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
return self.text.GetValue()
|
|
||||||
|
|
||||||
def set_image_description(self, desc):
|
|
||||||
self.image_description.Enable(True)
|
|
||||||
if len(self.image_description.GetValue()) == 0:
|
|
||||||
self.image_description.SetValue(desc)
|
|
||||||
else:
|
|
||||||
self.image_description.SetValue(self.image_description.GetValue()+"\n"+desc)
|
|
||||||
|
|
||||||
def text_focus(self):
|
|
||||||
self.text.SetFocus()
|
|
||||||
|
|
||||||
def onSelect(self, ev):
|
|
||||||
self.text.SelectAll()
|
|
||||||
|
|
||||||
def enable_button(self, buttonName):
|
|
||||||
if hasattr(self, buttonName):
|
|
||||||
return getattr(self, buttonName).Enable()
|
|
||||||
|
|
||||||
class viewNonTweet(widgetUtils.BaseDialog):
|
|
||||||
|
|
||||||
def __init__(self, text, date="", *args, **kwargs):
|
|
||||||
super(viewNonTweet, self).__init__(None, size=(850,850))
|
|
||||||
self.SetTitle(_(u"View"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
label = wx.StaticText(panel, -1, _(u"Item"))
|
|
||||||
self.text = wx.TextCtrl(parent=panel, id=-1, value=text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
|
||||||
dc = wx.WindowDC(self.text)
|
|
||||||
dc.SetFont(self.text.GetFont())
|
|
||||||
(x, y) = dc.GetMultiLineTextExtent("0"*140)
|
|
||||||
self.text.SetSize((x, y))
|
|
||||||
self.text.SetFocus()
|
|
||||||
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
textBox.Add(label, 0, wx.ALL, 5)
|
|
||||||
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
mainBox.Add(textBox, 0, wx.ALL, 5)
|
|
||||||
if date != "":
|
|
||||||
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
|
||||||
date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
|
||||||
dc = wx.WindowDC(date)
|
|
||||||
dc.SetFont(date.GetFont())
|
|
||||||
(x, y) = dc.GetTextExtent("0"*100)
|
|
||||||
date.SetSize((x, y))
|
|
||||||
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
|
||||||
dateBox.Add(date, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
|
||||||
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
|
||||||
self.share.Enable(False)
|
|
||||||
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
|
||||||
self.unshortenButton.Disable()
|
|
||||||
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
|
||||||
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
|
||||||
cancelButton.SetDefault()
|
|
||||||
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
|
||||||
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
|
||||||
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
|
||||||
selectId = wx.ID_ANY
|
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
|
||||||
])
|
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
|
||||||
panel.SetSizer(mainBox)
|
|
||||||
self.SetClientSize(mainBox.CalcMin())
|
|
||||||
|
|
||||||
def onSelect(self, ev):
|
|
||||||
self.text.SelectAll()
|
|
||||||
|
|
||||||
def set_text(self, text):
|
|
||||||
self.text.ChangeValue(text)
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
return self.text.GetValue()
|
|
||||||
|
|
||||||
def text_focus(self):
|
|
||||||
self.text.SetFocus()
|
|
||||||
|
|
||||||
def enable_button(self, buttonName):
|
|
||||||
if hasattr(self, buttonName):
|
|
||||||
return getattr(self, buttonName).Enable()
|
|
2
src/wxUI/dialogs/twitterDialogs/__init__.py
Normal file
2
src/wxUI/dialogs/twitterDialogs/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet, poll
|
||||||
|
from .templateDialogs import EditTemplateDialog
|
52
src/wxUI/dialogs/twitterDialogs/templateDialogs.py
Normal file
52
src/wxUI/dialogs/twitterDialogs/templateDialogs.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
import wx
|
||||||
|
import output
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
class EditTemplateDialog(wx.Dialog):
|
||||||
|
def __init__(self, template: str, variables: List[str] = [], default_template: str = "", *args, **kwds) -> None:
|
||||||
|
super(EditTemplateDialog, self).__init__(parent=None, title=_("Edit Template"), *args, **kwds)
|
||||||
|
self.default_template = default_template
|
||||||
|
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainSizer.Add(sizer_1, 1, wx.EXPAND, 0)
|
||||||
|
label_1 = wx.StaticText(self, wx.ID_ANY, _("Edit template"))
|
||||||
|
sizer_1.Add(label_1, 0, 0, 0)
|
||||||
|
self.template = wx.TextCtrl(self, wx.ID_ANY, template)
|
||||||
|
sizer_1.Add(self.template, 0, 0, 0)
|
||||||
|
sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Available variables")), wx.HORIZONTAL)
|
||||||
|
mainSizer.Add(sizer_2, 1, wx.EXPAND, 0)
|
||||||
|
self.variables = wx.ListBox(self, wx.ID_ANY, choices=["$"+v for v in variables])
|
||||||
|
self.variables.Bind(wx.EVT_CHAR_HOOK, self.on_keypress)
|
||||||
|
sizer_2.Add(self.variables, 0, 0, 0)
|
||||||
|
sizer_3 = wx.StdDialogButtonSizer()
|
||||||
|
mainSizer.Add(sizer_3, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
||||||
|
self.button_SAVE = wx.Button(self, wx.ID_SAVE)
|
||||||
|
self.button_SAVE.SetDefault()
|
||||||
|
sizer_3.AddButton(self.button_SAVE)
|
||||||
|
self.button_CANCEL = wx.Button(self, wx.ID_CANCEL)
|
||||||
|
sizer_3.AddButton(self.button_CANCEL)
|
||||||
|
self.button_RESTORE = wx.Button(self, wx.ID_ANY, _("Restore template"))
|
||||||
|
self.button_RESTORE.Bind(wx.EVT_BUTTON, self.on_restore)
|
||||||
|
sizer_3.AddButton(self.button_CANCEL)
|
||||||
|
sizer_3.Realize()
|
||||||
|
self.SetSizer(mainSizer)
|
||||||
|
mainSizer.Fit(self)
|
||||||
|
self.SetAffirmativeId(self.button_SAVE.GetId())
|
||||||
|
self.SetEscapeId(self.button_CANCEL.GetId())
|
||||||
|
self.Layout()
|
||||||
|
|
||||||
|
def on_keypress(self, event, *args, **kwargs):
|
||||||
|
if event.GetKeyCode() == wx.WXK_RETURN:
|
||||||
|
self.template.ChangeValue(self.template.GetValue()+self.variables.GetStringSelection()+", ")
|
||||||
|
output.speak(self.template.GetValue()+self.variables.GetStringSelection()+", ")
|
||||||
|
return
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
|
def on_restore(self, *args, **kwargs) -> None:
|
||||||
|
self.template.ChangeValue(self.default_template)
|
||||||
|
output.speak(_("Restored template to {}.").format(self.default_template))
|
||||||
|
self.template.SetFocus()
|
||||||
|
|
||||||
|
def invalid_template() -> None:
|
||||||
|
wx.MessageDialog(None, _("the template you have specified include variables that do not exists for the object. Please fix the template and try again. For your reference, you can see a list of all available variables in the variables list while editing your template."), _("Invalid template"), wx.ICON_ERROR).ShowModal()
|
542
src/wxUI/dialogs/twitterDialogs/tweetDialogs.py
Normal file
542
src/wxUI/dialogs/twitterDialogs/tweetDialogs.py
Normal file
@ -0,0 +1,542 @@
|
|||||||
|
""" GUI dialogs for tweet writing and displaying. """
|
||||||
|
import wx
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
class tweet(wx.Dialog):
|
||||||
|
def __init__(self, title: str, caption: str, message: str = "", max_length: int = 280, thread_mode: bool = True, *args, **kwds) -> None:
|
||||||
|
""" Creates the basic Tweet dialog. This might be considered the base class for other dialogs.
|
||||||
|
title str: title to be used in the dialog.
|
||||||
|
caption str: This is the text to be placed alongside the text field.
|
||||||
|
message str: Text to be inserted in the tweet.
|
||||||
|
max_length int: Maximum amount of characters the tweet will accept. By default is 280 chahracters.
|
||||||
|
thread_mode bool: If set to False, disables the button that allows to make threads by adding more tweets.
|
||||||
|
"""
|
||||||
|
super(tweet, self).__init__(parent=None, *args, **kwds)
|
||||||
|
self.SetTitle(title)
|
||||||
|
self.create_controls(max_length=max_length, caption=caption, message=message, thread_mode=thread_mode)
|
||||||
|
|
||||||
|
def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None:
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
text_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(text_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_1 = wx.StaticText(panel, wx.ID_ANY, caption)
|
||||||
|
text_sizer.Add(label_1, 0, 0, 0)
|
||||||
|
self.text = wx.TextCtrl(panel, wx.ID_ANY, "", size=(444, -1), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
||||||
|
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
||||||
|
text_sizer.Add(self.text, 1, wx.EXPAND, 0)
|
||||||
|
list_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(list_sizer, 1, wx.EXPAND, 0)
|
||||||
|
Attachment_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments"))
|
||||||
|
Attachment_sizer.Add(label_2, 0, 0, 0)
|
||||||
|
self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
||||||
|
self.attachments.AppendColumn(_("File"))
|
||||||
|
self.attachments.AppendColumn(_("Type"))
|
||||||
|
self.attachments.AppendColumn(_("Description"))
|
||||||
|
Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
||||||
|
self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment"))
|
||||||
|
self.remove_attachment.Enable(False)
|
||||||
|
Attachment_sizer.Add(self.remove_attachment, 0, 0, 0)
|
||||||
|
tweet_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
list_sizer.Add(tweet_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_3 = wx.StaticText(panel, wx.ID_ANY, _("Added Tweets"))
|
||||||
|
tweet_sizer.Add(label_3, 0, 0, 0)
|
||||||
|
self.tweets = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
||||||
|
self.tweets.AppendColumn(_("Text"))
|
||||||
|
self.tweets.AppendColumn(_("Attachments"))
|
||||||
|
self.tweets.Enable(False)
|
||||||
|
tweet_sizer.Add(self.tweets, 1, wx.EXPAND, 0)
|
||||||
|
self.remove_tweet = wx.Button(panel, wx.ID_ANY, _("Delete tweet"))
|
||||||
|
self.remove_tweet.Enable(False)
|
||||||
|
tweet_sizer.Add(self.remove_tweet, 0, 0, 0)
|
||||||
|
btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0)
|
||||||
|
self.add = wx.Button(panel, wx.ID_ANY, _("A&dd..."))
|
||||||
|
btn_sizer_1.Add(self.add, 0, 0, 0)
|
||||||
|
self.add_tweet = wx.Button(panel, wx.ID_ANY, _("Add t&weet"))
|
||||||
|
self.add_tweet.Enable(thread_mode)
|
||||||
|
btn_sizer_1.Add(self.add_tweet, 0, 0, 0)
|
||||||
|
self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio..."))
|
||||||
|
btn_sizer_1.Add(self.add_audio, 0, 0, 0)
|
||||||
|
btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0)
|
||||||
|
self.autocomplete_users = wx.Button(panel, wx.ID_ANY, _("Auto&complete users"))
|
||||||
|
btn_sizer_2.Add(self.autocomplete_users, 0, 0, 0)
|
||||||
|
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
||||||
|
btn_sizer_2.Add(self.spellcheck, 0, 0, 0)
|
||||||
|
self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate"))
|
||||||
|
btn_sizer_2.Add(self.translate, 0, 0, 0)
|
||||||
|
ok_cancel_sizer = wx.StdDialogButtonSizer()
|
||||||
|
mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
||||||
|
self.send = wx.Button(panel, wx.ID_OK, _("Sen&d"))
|
||||||
|
self.send.SetDefault()
|
||||||
|
ok_cancel_sizer.Add(self.send, 0, 0, 0)
|
||||||
|
self.cancel = wx.Button(panel, wx.ID_CANCEL, "")
|
||||||
|
ok_cancel_sizer.AddButton(self.cancel)
|
||||||
|
ok_cancel_sizer.Realize()
|
||||||
|
panel.SetSizer(mainBox)
|
||||||
|
self.Fit()
|
||||||
|
self.SetAffirmativeId(self.send.GetId())
|
||||||
|
self.SetEscapeId(self.cancel.GetId())
|
||||||
|
self.Layout()
|
||||||
|
|
||||||
|
def handle_keys(self, event: wx.Event, *args, **kwargs) -> None:
|
||||||
|
""" Allows to react to certain keyboard events from the text control. """
|
||||||
|
shift=event.ShiftDown()
|
||||||
|
if event.GetKeyCode() == wx.WXK_RETURN and shift==False and hasattr(self,'send'):
|
||||||
|
self.EndModal(wx.ID_OK)
|
||||||
|
else:
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
|
def reset_controls(self) -> None:
|
||||||
|
""" Resetss text control and attachments to their default, empty values. This is used while adding more tweets in a thread. """
|
||||||
|
self.text.ChangeValue("")
|
||||||
|
self.attachments.DeleteAllItems()
|
||||||
|
|
||||||
|
def add_item(self, list_type: str = "attachment", item: List[str] = []) -> None:
|
||||||
|
""" Adds an item to a list control. Item should be a list with the same amount of items for each column present in the ListCtrl. """
|
||||||
|
if list_type == "attachment":
|
||||||
|
self.attachments.Append(item)
|
||||||
|
else:
|
||||||
|
self.tweets.Append(item)
|
||||||
|
|
||||||
|
def remove_item(self, list_type: str = "attachment") -> None:
|
||||||
|
if list_type == "attachment":
|
||||||
|
item = self.attachments.GetFocusedItem()
|
||||||
|
if item > -1:
|
||||||
|
self.attachments.DeleteItem(item)
|
||||||
|
else:
|
||||||
|
item = self.tweets.GetFocusedItem()
|
||||||
|
if item > -1:
|
||||||
|
self.tweets.DeleteItem(item)
|
||||||
|
|
||||||
|
def attach_menu(self, event=None, enabled=True, *args, **kwargs):
|
||||||
|
menu = wx.Menu()
|
||||||
|
self.add_image = menu.Append(wx.ID_ANY, _("Image"))
|
||||||
|
self.add_image.Enable(enabled)
|
||||||
|
self.add_video = menu.Append(wx.ID_ANY, _("Video"))
|
||||||
|
self.add_video.Enable(enabled)
|
||||||
|
self.add_poll = menu.Append(wx.ID_ANY, _("Poll"))
|
||||||
|
self.add_poll.Enable(enabled)
|
||||||
|
return menu
|
||||||
|
|
||||||
|
def ask_description(self):
|
||||||
|
dlg = wx.TextEntryDialog(self, _(u"please provide a description"), _(u"Description"))
|
||||||
|
dlg.ShowModal()
|
||||||
|
result = dlg.GetValue()
|
||||||
|
dlg.Destroy()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_image(self):
|
||||||
|
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||||
|
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return (None, None)
|
||||||
|
dsc = self.ask_description()
|
||||||
|
return (openFileDialog.GetPath(), dsc)
|
||||||
|
|
||||||
|
def get_video(self):
|
||||||
|
openFileDialog = wx.FileDialog(self, _("Select the video to be uploaded"), "", "", _("Video files (*.mp4)|*.mp4"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||||
|
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return None
|
||||||
|
return openFileDialog.GetPath()
|
||||||
|
|
||||||
|
def unable_to_attach_file(self, *args, **kwargs):
|
||||||
|
return wx.MessageDialog(self, _("It is not possible to add more attachments. Please make sure your tweet complies with Twitter'S attachment rules. You can add only one video or GIF in every tweet, and a maximum of 4 photos."), _("Error adding attachment"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
class reply(tweet):
|
||||||
|
|
||||||
|
def __init__(self, users: List[str] = [], *args, **kwargs) -> None:
|
||||||
|
self.users = users
|
||||||
|
super(reply, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None:
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
text_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(text_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_1 = wx.StaticText(panel, wx.ID_ANY, caption)
|
||||||
|
text_sizer.Add(label_1, 0, 0, 0)
|
||||||
|
self.text = wx.TextCtrl(panel, wx.ID_ANY, "", size=(500, 200), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
||||||
|
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
||||||
|
text_sizer.Add(self.text, 1, wx.EXPAND, 0)
|
||||||
|
list_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(list_sizer, 1, wx.EXPAND, 0)
|
||||||
|
Attachment_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments"))
|
||||||
|
Attachment_sizer.Add(label_2, 0, 0, 0)
|
||||||
|
self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
||||||
|
self.attachments.AppendColumn(_("File"))
|
||||||
|
self.attachments.AppendColumn(_("Type"))
|
||||||
|
self.attachments.AppendColumn(_("Description"))
|
||||||
|
Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
||||||
|
self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment"))
|
||||||
|
self.remove_attachment.Enable(False)
|
||||||
|
Attachment_sizer.Add(self.remove_attachment, 0, 0, 0)
|
||||||
|
user_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
list_sizer.Add(user_sizer, 0, 0, 0)
|
||||||
|
self.mention_all = wx.CheckBox(panel, -1, _(u"&Mention to all"), size=wx.DefaultSize)
|
||||||
|
self.mention_all.Disable()
|
||||||
|
user_sizer.Add(self.mention_all, 0, wx.ALL, 5)
|
||||||
|
self.checkboxes = []
|
||||||
|
for i in self.users:
|
||||||
|
user_checkbox = wx.CheckBox(panel, -1, "@"+i, size=wx.DefaultSize)
|
||||||
|
self.checkboxes.append(user_checkbox)
|
||||||
|
user_sizer.Add(self.checkboxes[-1], 0, wx.ALL, 5)
|
||||||
|
btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0)
|
||||||
|
self.add = wx.Button(panel, wx.ID_ANY, _("A&dd..."))
|
||||||
|
btn_sizer_1.Add(self.add, 0, 0, 0)
|
||||||
|
self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio..."))
|
||||||
|
btn_sizer_1.Add(self.add_audio, 0, 0, 0)
|
||||||
|
btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0)
|
||||||
|
self.autocomplete_users = wx.Button(panel, wx.ID_ANY, _("Auto&complete users"))
|
||||||
|
btn_sizer_2.Add(self.autocomplete_users, 0, 0, 0)
|
||||||
|
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
||||||
|
btn_sizer_2.Add(self.spellcheck, 0, 0, 0)
|
||||||
|
self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate"))
|
||||||
|
btn_sizer_2.Add(self.translate, 0, 0, 0)
|
||||||
|
ok_cancel_sizer = wx.StdDialogButtonSizer()
|
||||||
|
mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
||||||
|
self.send = wx.Button(panel, wx.ID_OK, _("Sen&d"))
|
||||||
|
self.send.SetDefault()
|
||||||
|
ok_cancel_sizer.Add(self.send, 0, 0, 0)
|
||||||
|
self.cancel = wx.Button(panel, wx.ID_CANCEL, "")
|
||||||
|
ok_cancel_sizer.AddButton(self.cancel)
|
||||||
|
ok_cancel_sizer.Realize()
|
||||||
|
panel.SetSizer(mainBox)
|
||||||
|
self.Fit()
|
||||||
|
self.SetAffirmativeId(self.send.GetId())
|
||||||
|
self.SetEscapeId(self.cancel.GetId())
|
||||||
|
self.Layout()
|
||||||
|
|
||||||
|
def attach_menu(self, event=None, enabled=True, *args, **kwargs):
|
||||||
|
menu = wx.Menu()
|
||||||
|
self.add_image = menu.Append(wx.ID_ANY, _("Image"))
|
||||||
|
self.add_image.Enable(enabled)
|
||||||
|
self.add_video = menu.Append(wx.ID_ANY, _("Video"))
|
||||||
|
self.add_video.Enable(enabled)
|
||||||
|
return menu
|
||||||
|
|
||||||
|
class dm(tweet):
|
||||||
|
|
||||||
|
def __init__(self, users: List[str] = [], *args, **kwargs) -> None:
|
||||||
|
self.users = users
|
||||||
|
super(dm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None:
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
label_recipient = wx.StaticText(panel, -1, _("&Recipient"))
|
||||||
|
self.cb = wx.ComboBox(panel, -1, choices=self.users, value=self.users[0], size=wx.DefaultSize)
|
||||||
|
self.autocomplete_users = wx.Button(panel, -1, _(u"Auto&complete users"))
|
||||||
|
recipient_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
recipient_sizer.Add(label_recipient, 0, 0, 0)
|
||||||
|
recipient_sizer.Add(self.cb, 1, wx.EXPAND, 0)
|
||||||
|
mainBox.Add(recipient_sizer, 0, wx.EXPAND, 0)
|
||||||
|
text_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(text_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_1 = wx.StaticText(panel, wx.ID_ANY, caption)
|
||||||
|
text_sizer.Add(label_1, 0, 0, 0)
|
||||||
|
self.text = wx.TextCtrl(panel, wx.ID_ANY, "", size=(500, 200), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
|
||||||
|
self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text)
|
||||||
|
self.text.SetFocus()
|
||||||
|
text_sizer.Add(self.text, 1, wx.EXPAND, 0)
|
||||||
|
list_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(list_sizer, 1, wx.EXPAND, 0)
|
||||||
|
Attachment_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments"))
|
||||||
|
Attachment_sizer.Add(label_2, 0, 0, 0)
|
||||||
|
self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES)
|
||||||
|
self.attachments.AppendColumn(_("File"))
|
||||||
|
self.attachments.AppendColumn(_("Type"))
|
||||||
|
self.attachments.AppendColumn(_("Description"))
|
||||||
|
Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0)
|
||||||
|
self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment"))
|
||||||
|
self.remove_attachment.Enable(False)
|
||||||
|
Attachment_sizer.Add(self.remove_attachment, 0, 0, 0)
|
||||||
|
btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0)
|
||||||
|
self.add = wx.Button(panel, wx.ID_ANY, _("A&dd..."))
|
||||||
|
btn_sizer_1.Add(self.add, 0, 0, 0)
|
||||||
|
self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio..."))
|
||||||
|
btn_sizer_1.Add(self.add_audio, 0, 0, 0)
|
||||||
|
btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0)
|
||||||
|
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
||||||
|
btn_sizer_2.Add(self.spellcheck, 0, 0, 0)
|
||||||
|
self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate"))
|
||||||
|
btn_sizer_2.Add(self.translate, 0, 0, 0)
|
||||||
|
ok_cancel_sizer = wx.StdDialogButtonSizer()
|
||||||
|
mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
||||||
|
self.send = wx.Button(panel, wx.ID_OK, _("Sen&d"))
|
||||||
|
self.send.SetDefault()
|
||||||
|
ok_cancel_sizer.Add(self.send, 0, 0, 0)
|
||||||
|
self.cancel = wx.Button(panel, wx.ID_CANCEL, "")
|
||||||
|
ok_cancel_sizer.AddButton(self.cancel)
|
||||||
|
ok_cancel_sizer.Realize()
|
||||||
|
panel.SetSizer(mainBox)
|
||||||
|
self.Fit()
|
||||||
|
self.SetAffirmativeId(self.send.GetId())
|
||||||
|
self.SetEscapeId(self.cancel.GetId())
|
||||||
|
self.Layout()
|
||||||
|
|
||||||
|
def get_image(self):
|
||||||
|
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||||
|
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return (None, None)
|
||||||
|
return (openFileDialog.GetPath(), "")
|
||||||
|
|
||||||
|
def attach_menu(self, event=None, enabled=True, *args, **kwargs):
|
||||||
|
menu = wx.Menu()
|
||||||
|
self.add_image = menu.Append(wx.ID_ANY, _("Image"))
|
||||||
|
self.add_image.Enable(enabled)
|
||||||
|
self.add_video = menu.Append(wx.ID_ANY, _("Video"))
|
||||||
|
self.add_video.Enable(enabled)
|
||||||
|
return menu
|
||||||
|
|
||||||
|
class viewTweet(wx.Dialog):
|
||||||
|
def set_title(self, lenght):
|
||||||
|
self.SetTitle(_(u"Tweet - %i characters ") % (lenght,))
|
||||||
|
|
||||||
|
def __init__(self, text, rt_count, favs_count, source, date="", *args, **kwargs):
|
||||||
|
super(viewTweet, self).__init__(None, size=(850,850))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
label = wx.StaticText(panel, -1, _(u"Tweet"))
|
||||||
|
self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
|
dc = wx.WindowDC(self.text)
|
||||||
|
dc.SetFont(self.text.GetFont())
|
||||||
|
(x, y) = dc.GetMultiLineTextExtent("W"*280)
|
||||||
|
self.text.SetSize((x, y))
|
||||||
|
self.text.SetFocus()
|
||||||
|
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
textBox.Add(label, 0, wx.ALL, 5)
|
||||||
|
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
||||||
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
mainBox.Add(textBox, 0, wx.ALL, 5)
|
||||||
|
label2 = wx.StaticText(panel, -1, _(u"Image description"))
|
||||||
|
self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
|
dc = wx.WindowDC(self.image_description)
|
||||||
|
dc.SetFont(self.image_description.GetFont())
|
||||||
|
(x, y) = dc.GetMultiLineTextExtent("0"*450)
|
||||||
|
self.image_description.SetSize((x, y))
|
||||||
|
self.image_description.Enable(False)
|
||||||
|
iBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
iBox.Add(label2, 0, wx.ALL, 5)
|
||||||
|
iBox.Add(self.image_description, 1, wx.EXPAND, 5)
|
||||||
|
mainBox.Add(iBox, 0, wx.ALL, 5)
|
||||||
|
rtCountLabel = wx.StaticText(panel, -1, _(u"Retweets: "))
|
||||||
|
rtCount = wx.TextCtrl(panel, -1, rt_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
rtBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
rtBox.Add(rtCountLabel, 0, wx.ALL, 5)
|
||||||
|
rtBox.Add(rtCount, 0, wx.ALL, 5)
|
||||||
|
favsCountLabel = wx.StaticText(panel, -1, _(u"Likes: "))
|
||||||
|
favsCount = wx.TextCtrl(panel, -1, favs_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
favsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
favsBox.Add(favsCountLabel, 0, wx.ALL, 5)
|
||||||
|
favsBox.Add(favsCount, 0, wx.ALL, 5)
|
||||||
|
sourceLabel = wx.StaticText(panel, -1, _(u"Source: "))
|
||||||
|
sourceTweet = wx.TextCtrl(panel, -1, source, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
sourceBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sourceBox.Add(sourceLabel, 0, wx.ALL, 5)
|
||||||
|
sourceBox.Add(sourceTweet, 0, wx.ALL, 5)
|
||||||
|
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
||||||
|
dateTweet = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
dc = wx.WindowDC(dateTweet)
|
||||||
|
dc.SetFont(dateTweet.GetFont())
|
||||||
|
(x, y) = dc.GetTextExtent("0"*100)
|
||||||
|
dateTweet.SetSize((x, y))
|
||||||
|
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
||||||
|
dateBox.Add(dateTweet, 0, wx.ALL, 5)
|
||||||
|
infoBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
infoBox.Add(rtBox, 0, wx.ALL, 5)
|
||||||
|
infoBox.Add(favsBox, 0, wx.ALL, 5)
|
||||||
|
infoBox.Add(sourceBox, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(infoBox, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
||||||
|
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
||||||
|
self.share.Enable(False)
|
||||||
|
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
||||||
|
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
||||||
|
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
||||||
|
cancelButton.SetDefault()
|
||||||
|
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
||||||
|
selectId = wx.ID_ANY
|
||||||
|
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
||||||
|
self.accel_tbl = wx.AcceleratorTable([
|
||||||
|
(wx.ACCEL_CTRL, ord('A'), selectId),
|
||||||
|
])
|
||||||
|
self.SetAcceleratorTable(self.accel_tbl)
|
||||||
|
panel.SetSizer(mainBox)
|
||||||
|
self.SetClientSize(mainBox.CalcMin())
|
||||||
|
|
||||||
|
def set_text(self, text):
|
||||||
|
self.text.ChangeValue(text)
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
return self.text.GetValue()
|
||||||
|
|
||||||
|
def set_image_description(self, desc):
|
||||||
|
self.image_description.Enable(True)
|
||||||
|
if len(self.image_description.GetValue()) == 0:
|
||||||
|
self.image_description.SetValue(desc)
|
||||||
|
else:
|
||||||
|
self.image_description.SetValue(self.image_description.GetValue()+"\n"+desc)
|
||||||
|
|
||||||
|
def text_focus(self):
|
||||||
|
self.text.SetFocus()
|
||||||
|
|
||||||
|
def onSelect(self, ev):
|
||||||
|
self.text.SelectAll()
|
||||||
|
|
||||||
|
def enable_button(self, buttonName):
|
||||||
|
if hasattr(self, buttonName):
|
||||||
|
return getattr(self, buttonName).Enable()
|
||||||
|
|
||||||
|
class viewNonTweet(wx.Dialog):
|
||||||
|
|
||||||
|
def __init__(self, text, date="", *args, **kwargs):
|
||||||
|
super(viewNonTweet, self).__init__(None, size=(850,850))
|
||||||
|
self.SetTitle(_(u"View"))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
label = wx.StaticText(panel, -1, _(u"Item"))
|
||||||
|
self.text = wx.TextCtrl(parent=panel, id=-1, value=text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
|
dc = wx.WindowDC(self.text)
|
||||||
|
dc.SetFont(self.text.GetFont())
|
||||||
|
(x, y) = dc.GetMultiLineTextExtent("0"*140)
|
||||||
|
self.text.SetSize((x, y))
|
||||||
|
self.text.SetFocus()
|
||||||
|
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
textBox.Add(label, 0, wx.ALL, 5)
|
||||||
|
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
||||||
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
mainBox.Add(textBox, 0, wx.ALL, 5)
|
||||||
|
if date != "":
|
||||||
|
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
||||||
|
date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
dc = wx.WindowDC(date)
|
||||||
|
dc.SetFont(date.GetFont())
|
||||||
|
(x, y) = dc.GetTextExtent("0"*100)
|
||||||
|
date.SetSize((x, y))
|
||||||
|
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
||||||
|
dateBox.Add(date, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
||||||
|
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
||||||
|
self.share.Enable(False)
|
||||||
|
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
||||||
|
self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
|
||||||
|
self.unshortenButton.Disable()
|
||||||
|
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
||||||
|
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
||||||
|
cancelButton.SetDefault()
|
||||||
|
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
||||||
|
selectId = wx.ID_ANY
|
||||||
|
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
||||||
|
self.accel_tbl = wx.AcceleratorTable([
|
||||||
|
(wx.ACCEL_CTRL, ord('A'), selectId),
|
||||||
|
])
|
||||||
|
self.SetAcceleratorTable(self.accel_tbl)
|
||||||
|
panel.SetSizer(mainBox)
|
||||||
|
self.SetClientSize(mainBox.CalcMin())
|
||||||
|
|
||||||
|
def onSelect(self, ev):
|
||||||
|
self.text.SelectAll()
|
||||||
|
|
||||||
|
def set_text(self, text):
|
||||||
|
self.text.ChangeValue(text)
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
return self.text.GetValue()
|
||||||
|
|
||||||
|
def text_focus(self):
|
||||||
|
self.text.SetFocus()
|
||||||
|
|
||||||
|
def enable_button(self, buttonName):
|
||||||
|
if hasattr(self, buttonName):
|
||||||
|
return getattr(self, buttonName).Enable()
|
||||||
|
|
||||||
|
class poll(wx.Dialog):
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
super(poll, self).__init__(parent=None, id=wx.NewId(), title=_("Add a poll"))
|
||||||
|
sizer_1 = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
period_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer_1.Add(period_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_period = wx.StaticText(self, wx.ID_ANY, _("Participation time (in days)"))
|
||||||
|
period_sizer.Add(label_period, 0, 0, 0)
|
||||||
|
self.period = wx.SpinCtrl(self, wx.ID_ANY)
|
||||||
|
self.period.SetFocus()
|
||||||
|
self.period.SetRange(1, 7)
|
||||||
|
self.period.SetValue(7)
|
||||||
|
period_sizer.Add(self.period, 0, 0, 0)
|
||||||
|
sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Choices")), wx.VERTICAL)
|
||||||
|
sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
|
||||||
|
option1_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer_2.Add(option1_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_2 = wx.StaticText(self, wx.ID_ANY, _("Option 1"))
|
||||||
|
option1_sizer.Add(label_2, 0, 0, 0)
|
||||||
|
self.option1 = wx.TextCtrl(self, wx.ID_ANY, "")
|
||||||
|
self.option1.SetMaxLength(25)
|
||||||
|
option1_sizer.Add(self.option1, 0, 0, 0)
|
||||||
|
option2_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer_2.Add(option2_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_3 = wx.StaticText(self, wx.ID_ANY, _("Option 2"))
|
||||||
|
option2_sizer.Add(label_3, 0, 0, 0)
|
||||||
|
self.option2 = wx.TextCtrl(self, wx.ID_ANY, "")
|
||||||
|
self.option2.SetMaxLength(25)
|
||||||
|
option2_sizer.Add(self.option2, 0, 0, 0)
|
||||||
|
option3_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer_2.Add(option3_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_4 = wx.StaticText(self, wx.ID_ANY, _("Option 3"))
|
||||||
|
option3_sizer.Add(label_4, 0, 0, 0)
|
||||||
|
self.option3 = wx.TextCtrl(self, wx.ID_ANY, "")
|
||||||
|
self.option3.SetMaxLength(25)
|
||||||
|
option3_sizer.Add(self.option3, 0, 0, 0)
|
||||||
|
option4_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizer_2.Add(option4_sizer, 1, wx.EXPAND, 0)
|
||||||
|
label_5 = wx.StaticText(self, wx.ID_ANY, _("Option 4"))
|
||||||
|
option4_sizer.Add(label_5, 0, 0, 0)
|
||||||
|
self.option4 = wx.TextCtrl(self, wx.ID_ANY, "")
|
||||||
|
self.option4.SetMaxLength(25)
|
||||||
|
option4_sizer.Add(self.option4, 0, 0, 0)
|
||||||
|
btn_sizer = wx.StdDialogButtonSizer()
|
||||||
|
sizer_1.Add(btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
|
||||||
|
self.button_OK = wx.Button(self, wx.ID_OK)
|
||||||
|
self.button_OK.SetDefault()
|
||||||
|
self.button_OK.Bind(wx.EVT_BUTTON, self.validate_data)
|
||||||
|
btn_sizer.AddButton(self.button_OK)
|
||||||
|
self.button_CANCEL = wx.Button(self, wx.ID_CANCEL, "")
|
||||||
|
btn_sizer.AddButton(self.button_CANCEL)
|
||||||
|
btn_sizer.Realize()
|
||||||
|
self.SetSizer(sizer_1)
|
||||||
|
sizer_1.Fit(self)
|
||||||
|
self.SetAffirmativeId(self.button_OK.GetId())
|
||||||
|
self.SetEscapeId(self.button_CANCEL.GetId())
|
||||||
|
self.Layout()
|
||||||
|
|
||||||
|
def get_options(self):
|
||||||
|
controls = [self.option1, self.option2, self.option3, self.option4]
|
||||||
|
options = [option.GetValue() for option in controls if option.GetValue() != ""]
|
||||||
|
return options
|
||||||
|
|
||||||
|
def validate_data(self, *args, **kwargs):
|
||||||
|
options = self.get_options()
|
||||||
|
if len(options) < 2:
|
||||||
|
return wx.MessageDialog(self, _("Please make sure you have provided at least two options for the poll."), _("Not enough information"), wx.ICON_ERROR).ShowModal()
|
||||||
|
self.EndModal(wx.ID_OK)
|
@ -1,4 +1,4 @@
|
|||||||
{"current_version": "11",
|
{"current_version": "2021.02.23",
|
||||||
"description": "Snapshot version.",
|
"description": "Snapshot version.",
|
||||||
"date": "unknown",
|
"date": "unknown",
|
||||||
"downloads":
|
"downloads":
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 27a7c419835c5fed4ba77bc31d0e1aff50a78f2a
|
Subproject commit 1ee4175276e8dcb04c23cd0b76ff516d5986c698
|
Loading…
Reference in New Issue
Block a user