mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-10 06:46:07 -04:00
Sync upstream.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
name = 'TWBlue'
|
||||
snapshot = False
|
||||
if snapshot == False:
|
||||
version = "0.91"
|
||||
version = "0.92"
|
||||
update_url = 'http://twblue.es/updates/twblue_ngen.json'
|
||||
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
|
||||
else:
|
||||
|
@@ -28,21 +28,5 @@ def convert_soundcloud (url):
|
||||
else:
|
||||
raise TypeError('%r is not streamable' % url)
|
||||
|
||||
@matches_url('http://twup.me')
|
||||
def convert_twup(url):
|
||||
result = re.match("^http://twup.me/(?P<audio_id>[A-Za-z0-9]+/?)$", url, re.I)
|
||||
if not result or result.group("audio_id") is None:
|
||||
raise TypeError('%r is not a valid URL' % url)
|
||||
audio_id = result.group("audio_id")
|
||||
return 'http://twup.me/%s' % audio_id
|
||||
|
||||
#@matches_url('http://sndup.net')
|
||||
#def convert_sndup(url):
|
||||
# result = re.match("^http://sndup.net/(?P<audio_id>[a-z0-9]+/?)(|d|l|a)/?$", url, re.I)
|
||||
# if not result or result.group("audio_id") is None:
|
||||
# raise TypeError('%r is not a valid URL' % url)
|
||||
# audio_id = result.group("audio_id")
|
||||
# return 'http://sndup.net/%s/a' % audio_id
|
||||
|
||||
def convert_generic_audio(url):
|
||||
return url
|
||||
|
@@ -157,7 +157,7 @@ class bufferController(object):
|
||||
for i in attachments:
|
||||
photo = open(i["file"], "rb")
|
||||
img = self.session.twitter.twitter.upload_media(media=photo)
|
||||
self.session.twitter.twitter.set_description(media_id=img["media_id"], alt_text=dict(text=i["description"]))
|
||||
self.session.twitter.twitter.create_metadata(media_id=img["media_id"], alt_text=dict(text=i["description"]))
|
||||
media_ids.append(img["media_id"])
|
||||
self.session.twitter.twitter.update_status(status=text, media_ids=media_ids)
|
||||
|
||||
@@ -988,7 +988,7 @@ class searchBufferController(baseBufferController):
|
||||
# return None
|
||||
num = self.session.order_buffer(self.name, val)
|
||||
self.put_items_on_list(num)
|
||||
if num > 0:
|
||||
if num > 0 and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"]:
|
||||
self.session.sound.play("search_updated.ogg")
|
||||
return num
|
||||
|
||||
@@ -1068,7 +1068,7 @@ class searchPeopleBufferController(peopleBufferController):
|
||||
number_of_items = self.session.order_cursored_buffer(self.name, val)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if number_of_items > 0:
|
||||
if number_of_items > 0 and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"]:
|
||||
self.session.sound.play("search_updated.ogg")
|
||||
return number_of_items
|
||||
|
||||
@@ -1148,7 +1148,8 @@ class trendsBufferController(bufferController):
|
||||
self.name_ = data[0]["locations"][0]["name"]
|
||||
self.trends = data[0]["trends"]
|
||||
self.put_items_on_the_list()
|
||||
self.session.sound.play(self.sound)
|
||||
if self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"]:
|
||||
self.session.sound.play(self.sound)
|
||||
|
||||
def put_items_on_the_list(self):
|
||||
selected_item = self.buffer.list.get_selected()
|
||||
@@ -1270,7 +1271,7 @@ class conversationBufferController(searchBufferController):
|
||||
number_of_items = self.session.order_buffer(self.name, self.statuses)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if number_of_items > 0:
|
||||
if number_of_items > 0 and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"]:
|
||||
self.session.sound.play("search_updated.ogg")
|
||||
return number_of_items
|
||||
|
||||
|
@@ -477,12 +477,11 @@ class Controller(object):
|
||||
output.speak(_(u"Empty buffer."), True)
|
||||
return
|
||||
start = page.buffer.list.get_selected()
|
||||
for i in xrange(start,count):
|
||||
page.buffer.list.select_item(i)
|
||||
if string.lower() in page.get_message().lower():
|
||||
for i in xrange(start, count):
|
||||
if string.lower() in page.buffer.list.get_text_column(i, 1).lower():
|
||||
page.buffer.list.select_item(i)
|
||||
return output.speak(page.get_message(), True)
|
||||
output.speak(_(u"{0} not found.").format(string,), True)
|
||||
page.buffer.list.select_item(start)
|
||||
|
||||
def seekLeft(self, *args, **kwargs):
|
||||
try:
|
||||
@@ -931,8 +930,8 @@ class Controller(object):
|
||||
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (trends.get_string()), pos=pos)
|
||||
self.buffers.append(buffer)
|
||||
buffer.start_stream()
|
||||
timer = RepeatingTimer(300, buffer.start_stream)
|
||||
timer.start()
|
||||
buffer.timer = RepeatingTimer(300, buffer.start_stream)
|
||||
buffer.timer.start()
|
||||
buffer.session.settings["other_buffers"]["trending_topic_buffers"].append(woeid)
|
||||
buffer.session.settings.write()
|
||||
|
||||
@@ -987,9 +986,10 @@ class Controller(object):
|
||||
buff = self.view.search(buffer.name, buffer.account)
|
||||
answer = buffer.remove_buffer()
|
||||
if answer == False: return
|
||||
if hasattr(buff, "timer"):
|
||||
log.debug("destroying buffer...")
|
||||
if hasattr(buffer, "timer"):
|
||||
log.debug("Stopping timer...")
|
||||
buff.timer.cancel()
|
||||
buffer.timer.cancel()
|
||||
log.debug("Timer cancelled.")
|
||||
self.right()
|
||||
self.view.delete_buffer(buff)
|
||||
|
@@ -40,9 +40,8 @@ class basicTweet(object):
|
||||
dlg = translator.gui.translateDialog()
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
text_to_translate = self.message.get_text()
|
||||
source = [x[0] for x in translator.translator.available_languages()][dlg.get("source_lang")]
|
||||
dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
|
||||
msg = translator.translator.translate(text=text_to_translate, source=source, target=dest)
|
||||
msg = translator.translator.translate(text=text_to_translate, target=dest)
|
||||
self.message.set_text(msg)
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
|
@@ -1,11 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from yandex_translate import YandexTranslate
|
||||
|
||||
def translate(text="", source="auto", target="en"):
|
||||
def translate(text="", target="en"):
|
||||
t = YandexTranslate("trnsl.1.1.20161012T134532Z.d01b9c75fc39aa74.7d1be75a5166a80583eeb020e10f584168da6bf7")
|
||||
return t.translate(text, target)["text"][0]
|
||||
|
||||
vars = dict(text=text, lang=target)
|
||||
return t.translate(**vars)["text"][0]
|
||||
|
||||
supported_langs = None
|
||||
d = None
|
||||
languages = {
|
||||
"af": _(u"Afrikaans"),
|
||||
"sq": _(u"Albanian"),
|
||||
@@ -71,7 +73,7 @@ languages = {
|
||||
"ps": _(u"Pashto"),
|
||||
"fa": _(u"Persian"),
|
||||
"pl": _(u"Polish"),
|
||||
"pt-PT": _(u"Portuguese"),
|
||||
"pt": _(u"Portuguese"),
|
||||
"pa": _(u"Punjabi"),
|
||||
"ro": _(u"Romanian"),
|
||||
"ru": _(u"Russian"),
|
||||
@@ -101,8 +103,11 @@ languages = {
|
||||
}
|
||||
|
||||
def available_languages():
|
||||
l = languages.keys()
|
||||
d = languages.values()
|
||||
l.insert(0, '')
|
||||
d.insert(0, _(u"autodetect"))
|
||||
return sorted(zip(l, d))
|
||||
global supported_langs, d
|
||||
if supported_langs == None and d == None:
|
||||
t = YandexTranslate("trnsl.1.1.20161012T134532Z.d01b9c75fc39aa74.7d1be75a5166a80583eeb020e10f584168da6bf7")
|
||||
supported_langs = t.langs
|
||||
d = []
|
||||
for i in supported_langs:
|
||||
d.append(languages[i])
|
||||
return sorted(zip(supported_langs, d))
|
||||
|
@@ -25,15 +25,11 @@ class translateDialog(baseDialog.BaseWXDialog):
|
||||
super(translateDialog, self).__init__(None, -1, title=_(u"Translate message"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
staticSource = wx.StaticText(panel, -1, _(u"Source language"))
|
||||
self.source_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
|
||||
self.source_lang.SetFocus()
|
||||
staticDest = wx.StaticText(panel, -1, _(u"Target language"))
|
||||
self.source_lang.SetSelection(0)
|
||||
self.dest_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
|
||||
self.dest_lang.SetFocus()
|
||||
self.dest_lang.SetSelection(0)
|
||||
listSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listSizer.Add(staticSource)
|
||||
listSizer.Add(self.source_lang)
|
||||
listSizer.Add(staticDest)
|
||||
listSizer.Add(self.dest_lang)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" The main session object. Here are the twitter functions to interact with the "model" of TWBlue."""
|
||||
import wx
|
||||
import urllib2
|
||||
import config
|
||||
import twitter
|
||||
@@ -16,10 +17,11 @@ import config_utils
|
||||
import shelve
|
||||
import application
|
||||
import os
|
||||
from mysc.thread_utils import stream_threaded
|
||||
from mysc.thread_utils import stream_threaded, call_threaded
|
||||
from pubsub import pub
|
||||
log = logging.getLogger("sessionmanager.session")
|
||||
from long_tweets import tweets, twishort
|
||||
from wxUI import authorisationDialog
|
||||
|
||||
sessions = {}
|
||||
|
||||
@@ -166,7 +168,20 @@ class Session(object):
|
||||
if self.logged == True:
|
||||
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
||||
else:
|
||||
self.twitter.authorise(self.settings)
|
||||
self.authorisation_thread = call_threaded(self.twitter.authorise, self.settings)
|
||||
self.authorisation_dialog = authorisationDialog()
|
||||
self.authorisation_dialog.cancel.Bind(wx.EVT_BUTTON, self.authorisation_cancelled)
|
||||
pub.subscribe(self.authorisation_accepted, "authorisation-accepted")
|
||||
self.authorisation_dialog.ShowModal()
|
||||
|
||||
def authorisation_cancelled(self, *args, **kwargs):
|
||||
pub.sendMessage("authorisation-cancelled")
|
||||
self.authorisation_dialog.Destroy()
|
||||
del self.authorisation_dialog
|
||||
|
||||
def authorisation_accepted(self):
|
||||
pub.unsubscribe(self.authorisation_accepted, "authorisation-accepted")
|
||||
self.authorisation_dialog.Destroy()
|
||||
|
||||
def get_more_items(self, update_function, users=False, name=None, *args, **kwargs):
|
||||
results = []
|
||||
|
@@ -73,4 +73,17 @@ class sessionManagerWindow(wx.Dialog):
|
||||
self.configuration.Hide()
|
||||
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
self.Destroy()
|
||||
|
||||
class authorisationDialog(wx.Dialog):
|
||||
def __init__(self):
|
||||
super(authorisationDialog, self).__init__(parent=None, title=_(u"Authorising account..."))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.text = wx.TextCtrl(panel, -1, _("Waiting for account authorisation..."), style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
self.cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizer.Add(self.text, 0, wx.ALL, 5)
|
||||
sizer.Add(self.cancel, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
min = sizer.CalcMin()
|
||||
self.SetClientSize(min)
|
@@ -104,7 +104,7 @@ class soundSystem(object):
|
||||
sound_object.play()
|
||||
|
||||
class URLStream(object):
|
||||
def __init__(self,url=None):
|
||||
def __init__(self, url=None):
|
||||
self.url = url
|
||||
self.prepared = False
|
||||
log.debug("URL Player initialized")
|
||||
@@ -118,12 +118,10 @@ class URLStream(object):
|
||||
transformer = audio_services.find_url_transformer(self.url)
|
||||
self.url = transformer(self.url)
|
||||
log.debug("Transformed URL: %s. Prepared" % (self.url,))
|
||||
self.prepared = True
|
||||
else:
|
||||
self.url = url
|
||||
log.debug("Transformed URL: %s. Prepared" % (self.url,))
|
||||
self.prepared = True
|
||||
|
||||
self.prepared = True
|
||||
|
||||
def seek(self,step):
|
||||
pos=self.stream.get_position()
|
||||
@@ -154,7 +152,7 @@ class URLStream(object):
|
||||
self.stream.volume = float(volume)
|
||||
self.stream.play()
|
||||
log.debug("played")
|
||||
# call_threaded(self.delete_when_done)
|
||||
self.prepared=False
|
||||
|
||||
def stop_audio(self,delete=False):
|
||||
if hasattr(self, "stream"):
|
||||
@@ -167,6 +165,7 @@ class URLStream(object):
|
||||
if delete:
|
||||
del self.stream
|
||||
log.debug("Deleted audio stream.")
|
||||
self.prepared=False
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@@ -2,11 +2,12 @@
|
||||
import BaseHTTPServer
|
||||
import application
|
||||
from urlparse import urlparse, parse_qs
|
||||
from pubsub import pub
|
||||
|
||||
logged = False
|
||||
verifier = None
|
||||
|
||||
class handler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
class handler(BaseHTTPServer.BaseHTTPRequestHandler, object):
|
||||
|
||||
def do_GET(self):
|
||||
global logged
|
||||
@@ -18,4 +19,14 @@ class handler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
global verifier
|
||||
verifier = params.get('oauth_verifier', [None])[0]
|
||||
self.wfile.write(u"You have successfully logged into Twitter with {0}. You can close this window now.".format(application.name))
|
||||
pub.sendMessage("authorisation-accepted")
|
||||
pub.unsubscribe(self.cancelled, "authorisation-cancelled")
|
||||
self.finish()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pub.subscribe(self.cancelled, "authorisation-cancelled")
|
||||
super(handler, self).__init__(*args, **kwargs)
|
||||
|
||||
def cancelled(self):
|
||||
pub.unsubscribe(self.cancelled, "authorisation-cancelled")
|
||||
self.finish()
|
@@ -184,7 +184,7 @@ def compose_event(data, username, show_screen_names=False):
|
||||
else: event = _(u"%s(@%s) has removed you from the list %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["name"])
|
||||
elif data["event"] == "list_user_subscribed":
|
||||
if data["source"]["screen_name"] == username: event = _(u"You've subscribed to the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["target"]["name"], data["target"]["screen_name"])
|
||||
else: event = _(u"%s(@%s) has suscribed you to the list %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["name"])
|
||||
else: event = _(u"%s(@%s) has subscribed you to the list %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["name"])
|
||||
elif data["event"] == "list_user_unsubscribed":
|
||||
if data["source"]["screen_name"] == username: event = _(u"You've unsubscribed from the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["target"]["name"], data["target"]["screen_name"])
|
||||
else: event = _("You've been unsubscribed from the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["source"]["name"], data["source"]["screen_name"])
|
||||
|
@@ -29,7 +29,12 @@ def find_urls (tweet):
|
||||
i = "full_text"
|
||||
else:
|
||||
i = "text"
|
||||
return [s[0] for s in url_re.findall(tweet[i])]
|
||||
shorten_urls = find_urls_in_text(tweet[i])
|
||||
for url in range(0, len(shorten_urls)):
|
||||
try:
|
||||
urls.append(tweet["entities"]["urls"][url]["expanded_url"])
|
||||
except: pass
|
||||
return urls
|
||||
|
||||
def find_item(id, listItem):
|
||||
for i in range(0, len(listItem)):
|
||||
|
@@ -19,7 +19,7 @@ Questions, comments? ryan@venodesigns.net
|
||||
"""
|
||||
|
||||
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
|
||||
__version__ = '3.3.0'
|
||||
__version__ = '3.6.0'
|
||||
|
||||
from .api import Twython
|
||||
from .streaming import TwythonStreamer
|
||||
|
@@ -145,6 +145,7 @@ class Twython(EndpointsMixin, object):
|
||||
else:
|
||||
params = params
|
||||
files = list()
|
||||
|
||||
requests_args = {}
|
||||
for k, v in self.client_args.items():
|
||||
# Maybe this should be set as a class variable and only done once?
|
||||
@@ -195,17 +196,16 @@ class Twython(EndpointsMixin, object):
|
||||
error_message,
|
||||
error_code=response.status_code,
|
||||
retry_after=response.headers.get('X-Rate-Limit-Reset'))
|
||||
|
||||
content=""
|
||||
try:
|
||||
if response.status_code == 204:
|
||||
content = response.content
|
||||
else:
|
||||
content = response.json()
|
||||
except ValueError:
|
||||
# Send the response as is for working with /media/metadata/create.json.
|
||||
content = response.content
|
||||
# raise TwythonError('Response was not valid JSON. \
|
||||
# Unable to decode.')
|
||||
if response.content!="":
|
||||
raise TwythonError('Response was not valid JSON. \
|
||||
Unable to decode.')
|
||||
|
||||
return content
|
||||
|
||||
@@ -258,8 +258,10 @@ class Twython(EndpointsMixin, object):
|
||||
url = endpoint
|
||||
else:
|
||||
url = '%s/%s.json' % (self.api_url % version, endpoint)
|
||||
|
||||
content = self._request(url, method=method, params=params,
|
||||
api_call=url)
|
||||
|
||||
return content
|
||||
|
||||
def get(self, endpoint, params=None, version='1.1'):
|
||||
@@ -473,6 +475,11 @@ class Twython(EndpointsMixin, object):
|
||||
>>> print result
|
||||
|
||||
"""
|
||||
if not callable(function):
|
||||
raise TypeError('.cursor() takes a Twython function as its first \
|
||||
argument. Did you provide the result of a \
|
||||
function call?')
|
||||
|
||||
if not hasattr(function, 'iter_mode'):
|
||||
raise TwythonError('Unable to create generator for Twython \
|
||||
method "%s"' % function.__name__)
|
||||
@@ -531,7 +538,7 @@ class Twython(EndpointsMixin, object):
|
||||
|
||||
@staticmethod
|
||||
def html_for_tweet(tweet, use_display_url=True, use_expanded_url=False, expand_quoted_status=False):
|
||||
"""Return HTML for a tweet (urls, mentions, hashtags replaced with links)
|
||||
"""Return HTML for a tweet (urls, mentions, hashtags, symbols replaced with links)
|
||||
|
||||
:param tweet: Tweet object from received from Twitter API
|
||||
:param use_display_url: Use display URL to represent link
|
||||
@@ -547,62 +554,116 @@ class Twython(EndpointsMixin, object):
|
||||
if 'retweeted_status' in tweet:
|
||||
tweet = tweet['retweeted_status']
|
||||
|
||||
if 'extended_tweet' in tweet:
|
||||
tweet = tweet['extended_tweet']
|
||||
|
||||
orig_tweet_text = tweet.get('full_text') or tweet['text']
|
||||
|
||||
display_text_range = tweet.get('display_text_range') or [0, len(orig_tweet_text)]
|
||||
display_text_start, display_text_end = display_text_range[0], display_text_range[1]
|
||||
display_text = orig_tweet_text[display_text_start:display_text_end]
|
||||
prefix_text = orig_tweet_text[0:display_text_start]
|
||||
suffix_text = orig_tweet_text[display_text_end:len(orig_tweet_text)]
|
||||
|
||||
if 'entities' in tweet:
|
||||
text = tweet['text']
|
||||
entities = tweet['entities']
|
||||
# We'll put all the bits of replacement HTML and their starts/ends
|
||||
# in this list:
|
||||
entities = []
|
||||
|
||||
# Mentions
|
||||
for entity in sorted(entities['user_mentions'],
|
||||
key=lambda mention: len(mention['screen_name']), reverse=True):
|
||||
start, end = entity['indices'][0], entity['indices'][1]
|
||||
if 'user_mentions' in tweet['entities']:
|
||||
for entity in tweet['entities']['user_mentions']:
|
||||
temp = {}
|
||||
temp['start'] = entity['indices'][0]
|
||||
temp['end'] = entity['indices'][1]
|
||||
|
||||
mention_html = '<a href="https://twitter.com/%(screen_name)s" class="twython-mention">@%(screen_name)s</a>'
|
||||
text = re.sub(r'(?<!>)' + tweet['text'][start:end] + '(?!</a>)',
|
||||
mention_html % {'screen_name': entity['screen_name']}, text)
|
||||
mention_html = '<a href="https://twitter.com/%(screen_name)s" class="twython-mention">@%(screen_name)s</a>' % {'screen_name': entity['screen_name']}
|
||||
|
||||
if display_text_start <= temp['start'] <= display_text_end:
|
||||
temp['replacement'] = mention_html
|
||||
entities.append(temp)
|
||||
else:
|
||||
# Make the '@username' at the start, before
|
||||
# display_text, into a link:
|
||||
sub_expr = r'(?<!>)' + orig_tweet_text[temp['start']:temp['end']] + '(?!</a>)'
|
||||
prefix_text = re.sub(sub_expr, mention_html, prefix_text)
|
||||
|
||||
# Hashtags
|
||||
for entity in sorted(entities['hashtags'],
|
||||
key=lambda hashtag: len(hashtag['text']), reverse=True):
|
||||
start, end = entity['indices'][0], entity['indices'][1]
|
||||
if 'hashtags' in tweet['entities']:
|
||||
for entity in tweet['entities']['hashtags']:
|
||||
temp = {}
|
||||
temp['start'] = entity['indices'][0]
|
||||
temp['end'] = entity['indices'][1]
|
||||
|
||||
hashtag_html = '<a href="https://twitter.com/search?q=%%23%(hashtag)s" class="twython-hashtag">#%(hashtag)s</a>'
|
||||
text = re.sub(r'(?<!>)' + tweet['text'][start:end] + '(?!</a>)',
|
||||
hashtag_html % {'hashtag': entity['text']}, text)
|
||||
url_html = '<a href="https://twitter.com/search?q=%%23%(hashtag)s" class="twython-hashtag">#%(hashtag)s</a>' % {'hashtag': entity['text']}
|
||||
|
||||
# Urls
|
||||
for entity in entities['urls']:
|
||||
start, end = entity['indices'][0], entity['indices'][1]
|
||||
if use_display_url and entity.get('display_url') \
|
||||
and not use_expanded_url:
|
||||
shown_url = entity['display_url']
|
||||
elif use_expanded_url and entity.get('expanded_url'):
|
||||
shown_url = entity['expanded_url']
|
||||
else:
|
||||
shown_url = entity['url']
|
||||
temp['replacement'] = url_html
|
||||
entities.append(temp)
|
||||
|
||||
url_html = '<a href="%s" class="twython-url">%s</a>'
|
||||
text = text.replace(tweet['text'][start:end],
|
||||
url_html % (entity['url'], shown_url))
|
||||
# Symbols
|
||||
if 'symbols' in tweet['entities']:
|
||||
for entity in tweet['entities']['symbols']:
|
||||
temp = {}
|
||||
temp['start'] = entity['indices'][0]
|
||||
temp['end'] = entity['indices'][1]
|
||||
|
||||
# Media
|
||||
if 'media' in entities:
|
||||
for entity in entities['media']:
|
||||
start, end = entity['indices'][0], entity['indices'][1]
|
||||
if use_display_url and entity.get('display_url') \
|
||||
and not use_expanded_url:
|
||||
url_html = '<a href="https://twitter.com/search?q=%%24%(symbol)s" class="twython-symbol">$%(symbol)s</a>' % {'symbol': entity['text']}
|
||||
|
||||
temp['replacement'] = url_html
|
||||
entities.append(temp)
|
||||
|
||||
# URLs
|
||||
if 'urls' in tweet['entities']:
|
||||
for entity in tweet['entities']['urls']:
|
||||
temp = {}
|
||||
temp['start'] = entity['indices'][0]
|
||||
temp['end'] = entity['indices'][1]
|
||||
|
||||
if use_display_url and entity.get('display_url') and not use_expanded_url:
|
||||
shown_url = entity['display_url']
|
||||
elif use_expanded_url and entity.get('expanded_url'):
|
||||
shown_url = entity['expanded_url']
|
||||
else:
|
||||
shown_url = entity['url']
|
||||
|
||||
url_html = '<a href="%s" class="twython-media">%s</a>'
|
||||
text = text.replace(tweet['text'][start:end],
|
||||
url_html % (entity['url'], shown_url))
|
||||
url_html = '<a href="%s" class="twython-url">%s</a>' % (entity['url'], shown_url)
|
||||
|
||||
if expand_quoted_status and tweet.get('is_quote_status'):
|
||||
if display_text_start <= temp['start'] <= display_text_end:
|
||||
temp['replacement'] = url_html
|
||||
entities.append(temp)
|
||||
else:
|
||||
suffix_text = suffix_text.replace(orig_tweet_text[temp['start']:temp['end']], url_html)
|
||||
|
||||
if 'media' in tweet['entities']:
|
||||
for entity in tweet['entities']['media']:
|
||||
temp = {}
|
||||
temp['start'] = entity['indices'][0]
|
||||
temp['end'] = entity['indices'][1]
|
||||
|
||||
if use_display_url and entity.get('display_url') and not use_expanded_url:
|
||||
shown_url = entity['display_url']
|
||||
elif use_expanded_url and entity.get('expanded_url'):
|
||||
shown_url = entity['expanded_url']
|
||||
else:
|
||||
shown_url = entity['url']
|
||||
|
||||
url_html = '<a href="%s" class="twython-media">%s</a>' % (entity['url'], shown_url)
|
||||
|
||||
if display_text_start <= temp['start'] <= display_text_end:
|
||||
temp['replacement'] = url_html
|
||||
entities.append(temp)
|
||||
else:
|
||||
suffix_text = suffix_text.replace(orig_tweet_text[temp['start']:temp['end']], url_html)
|
||||
|
||||
# Now do all the replacements, starting from the end, so that the
|
||||
# start/end indices still work:
|
||||
for entity in sorted(entities, key=lambda e: e['start'], reverse=True):
|
||||
display_text = display_text[0:entity['start']] + entity['replacement'] + display_text[entity['end']:]
|
||||
|
||||
quote_text = ''
|
||||
if expand_quoted_status and tweet.get('is_quote_status') and tweet.get('quoted_status'):
|
||||
quoted_status = tweet['quoted_status']
|
||||
text += '<blockquote class="twython-quote">%(quote)s<cite><a href="%(quote_tweet_link)s">' \
|
||||
quote_text += '<blockquote class="twython-quote">%(quote)s<cite><a href="%(quote_tweet_link)s">' \
|
||||
'<span class="twython-quote-user-name">%(quote_user_name)s</span>' \
|
||||
'<span class="twython-quote-user-screenname">@%(quote_user_screen_name)s</span></a>' \
|
||||
'</cite></blockquote>' % \
|
||||
@@ -612,4 +673,9 @@ class Twython(EndpointsMixin, object):
|
||||
'quote_user_name': quoted_status['user']['name'],
|
||||
'quote_user_screen_name': quoted_status['user']['screen_name']}
|
||||
|
||||
return text
|
||||
return '%(prefix)s%(display)s%(suffix)s%(quote)s' % {
|
||||
'prefix': '<span class="twython-tweet-prefix">%s</span>' % prefix_text if prefix_text else '',
|
||||
'display': display_text,
|
||||
'suffix': '<span class="twython-tweet-suffix">%s</span>' % suffix_text if suffix_text else '',
|
||||
'quote': quote_text
|
||||
}
|
||||
|
@@ -17,10 +17,12 @@ https://dev.twitter.com/docs/api/1.1
|
||||
import json
|
||||
import os
|
||||
import warnings
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
from io import BytesIO
|
||||
from time import sleep
|
||||
#try:
|
||||
#from StringIO import StringIO
|
||||
#except ImportError:
|
||||
#from io import StringIO
|
||||
|
||||
from .advisory import TwythonDeprecationWarning
|
||||
|
||||
@@ -143,15 +145,20 @@ class EndpointsMixin(object):
|
||||
Docs:
|
||||
https://dev.twitter.com/rest/reference/post/media/upload
|
||||
"""
|
||||
# https://dev.twitter.com/rest/reference/get/media/upload-status
|
||||
if params and params.get('command', '') == 'STATUS':
|
||||
return self.get('https://upload.twitter.com/1.1/media/upload.json', params=params)
|
||||
|
||||
return self.post('https://upload.twitter.com/1.1/media/upload.json', params=params)
|
||||
|
||||
def set_description(self, **params):
|
||||
""" Adds a description to an image."""
|
||||
# This method only accepts strings, no dictionaries.
|
||||
def create_metadata(self, **params):
|
||||
""" Adds metadata to a media element, such as image descriptions for visually impaired.
|
||||
Docs: https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create
|
||||
"""
|
||||
params = json.dumps(params)
|
||||
return self.post("media/metadata/create", params=params)
|
||||
return self.post("https://upload.twitter.com/1.1/media/metadata/create.json", params=params)
|
||||
|
||||
def upload_video(self, media, media_type, size=None):
|
||||
def upload_video(self, media, media_type, media_category=None, size=None, check_progress=False):
|
||||
"""Uploads video file to Twitter servers in chunks. The file will be available to be attached
|
||||
to a status for 60 minutes. To attach to a update, pass a list of returned media ids
|
||||
to the 'update_status' method using the 'media_ids' param.
|
||||
@@ -176,7 +183,8 @@ class EndpointsMixin(object):
|
||||
params = {
|
||||
'command': 'INIT',
|
||||
'media_type': media_type,
|
||||
'total_bytes': size
|
||||
'total_bytes': size,
|
||||
'media_category': media_category
|
||||
}
|
||||
response_init = self.post(upload_url, params=params)
|
||||
media_id = response_init['media_id']
|
||||
@@ -187,7 +195,7 @@ class EndpointsMixin(object):
|
||||
data = media.read(1*1024*1024)
|
||||
if not data:
|
||||
break
|
||||
media_chunk = StringIO()
|
||||
media_chunk = BytesIO()
|
||||
media_chunk.write(data)
|
||||
media_chunk.seek(0)
|
||||
|
||||
@@ -205,7 +213,38 @@ class EndpointsMixin(object):
|
||||
'command': 'FINALIZE',
|
||||
'media_id': media_id
|
||||
}
|
||||
return self.post(upload_url, params=params)
|
||||
|
||||
response = self.post(upload_url, params=params)
|
||||
|
||||
# Only get the status if explicity asked to
|
||||
# Default to False
|
||||
if check_progress:
|
||||
|
||||
# Stage 4: STATUS call if still processing
|
||||
params = {
|
||||
'command': 'STATUS',
|
||||
'media_id': media_id
|
||||
}
|
||||
|
||||
# added code to handle if media_category is NOT set and check_progress=True
|
||||
# the API will return a NoneType object in this case
|
||||
try:
|
||||
processing_state = response.get('processing_info').get('state')
|
||||
except AttributeError:
|
||||
return response
|
||||
|
||||
if processing_state:
|
||||
while (processing_state == 'pending' or processing_state == 'in_progress') :
|
||||
# get the secs to wait
|
||||
check_after_secs = response.get('processing_info').get('check_after_secs')
|
||||
|
||||
if check_after_secs:
|
||||
sleep(check_after_secs)
|
||||
response = self.get(upload_url, params=params)
|
||||
# get new state after waiting
|
||||
processing_state = response.get('processing_info').get('state')
|
||||
|
||||
return response
|
||||
|
||||
def get_oembed_tweet(self, **params):
|
||||
"""Returns information allowing the creation of an embedded
|
||||
|
@@ -10,11 +10,12 @@ logger = logging.getLogger("updater")
|
||||
|
||||
def do_update(endpoint=application.update_url):
|
||||
try:
|
||||
update.perform_update(endpoint=endpoint, current_version=application.version, app_name=application.name, update_available_callback=available_update_dialog, progress_callback=progress_callback, update_complete_callback=update_finished)
|
||||
result = update.perform_update(endpoint=endpoint, current_version=application.version, app_name=application.name, update_available_callback=available_update_dialog, progress_callback=progress_callback, update_complete_callback=update_finished)
|
||||
except:
|
||||
if endpoint == application.update_url:
|
||||
logger.error("Update failed! Using mirror URL...")
|
||||
return do_update(endpoint=application.mirror_update_url)
|
||||
else:
|
||||
logger.exception("Update failed.")
|
||||
output.speak("An exception occurred while attempting to update " + application.name + ". If this message persists, contact the " + application.name + " developers. More information about the exception has been written to the error log.",True)
|
||||
output.speak("An exception occurred while attempting to update " + application.name + ". If this message persists, contact the " + application.name + " developers. More information about the exception has been written to the error log.",True)
|
||||
return result
|
@@ -25,7 +25,7 @@ def unshorten (url, service=None, **kwargs):
|
||||
|
||||
|
||||
def default_service ():
|
||||
return shorteners.TinyurlShortener
|
||||
return shorteners.AcortameShortener
|
||||
|
||||
def find_service (service, **kwargs):
|
||||
for i in shorteners.__all__:
|
||||
|
@@ -6,4 +6,5 @@ from tinyarrows import TinyArrowsShortener
|
||||
from tinyurl import TinyurlShortener
|
||||
from xedcc import XedccShortener
|
||||
from clckru import ClckruShortener
|
||||
__all__ = ["HKCShortener", "IsgdShortener", "OnjmeShortener", "TinyArrowsShortener", "TinyurlShortener", "XedccShortener", "ClckruShortener"]
|
||||
from acortame import AcortameShortener
|
||||
__all__ = ["HKCShortener", "IsgdShortener", "OnjmeShortener", "TinyArrowsShortener", "TinyurlShortener", "XedccShortener", "ClckruShortener", "AcortameShortener"]
|
||||
|
27
src/url_shortener/shorteners/acortame.py
Normal file
27
src/url_shortener/shorteners/acortame.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from url_shortener import URLShortener
|
||||
import requests
|
||||
import urllib
|
||||
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.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.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
@@ -1,19 +1,18 @@
|
||||
import urllib
|
||||
|
||||
import requests
|
||||
from url_shortener import URLShortener
|
||||
|
||||
|
||||
class ClckruShortener (URLShortener):
|
||||
def __init__ (self, *args, **kwargs):
|
||||
self.name = "clck.ru"
|
||||
return super(ClckruShortener, self).__init__(*args, **kwargs)
|
||||
super(ClckruShortener, self).__init__(*args, **kwargs)
|
||||
|
||||
def _shorten (self, url):
|
||||
answer = url
|
||||
api = urllib.urlopen ("http://clck.ru/--?url=" + urllib.quote(url))
|
||||
if api.getcode() == 200:
|
||||
answer = api.read()
|
||||
api.close()
|
||||
api = requests.get ("http://clck.ru/--?url=" + urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
||||
|
||||
def created_url (self, url):
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import urllib
|
||||
|
||||
import requests
|
||||
from url_shortener import URLShortener
|
||||
|
||||
class HKCShortener (URLShortener):
|
||||
@@ -9,10 +9,9 @@ class HKCShortener (URLShortener):
|
||||
|
||||
def _shorten (self, url):
|
||||
answer = url
|
||||
api = urllib.urlopen ("http://hkc.im/yourls-api.php?action=shorturl&format=simple&url=" + urllib.quote(url))
|
||||
if api.getcode() == 200:
|
||||
answer = api.read()
|
||||
api.close()
|
||||
api = requests.get ("http://hkc.im/yourls-api.php?action=shorturl&format=simple&url=" + urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
||||
|
||||
def created_url (self, url):
|
||||
|
@@ -1,19 +1,18 @@
|
||||
import urllib
|
||||
|
||||
import requests
|
||||
from url_shortener import URLShortener
|
||||
|
||||
|
||||
class IsgdShortener (URLShortener):
|
||||
def __init__ (self, *args, **kwargs):
|
||||
self.name = "Is.gd"
|
||||
return super(IsgdShortener, self).__init__(*args, **kwargs)
|
||||
super(IsgdShortener, self).__init__(*args, **kwargs)
|
||||
|
||||
def _shorten (self, url):
|
||||
answer = url
|
||||
api = urllib.urlopen ("http://is.gd/api.php?longurl=" + urllib.quote(url))
|
||||
if api.getcode() == 200:
|
||||
answer = api.read()
|
||||
api.close()
|
||||
api = requests.get ("http://is.gd/api.php?longurl=" + urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
||||
|
||||
def created_url (self, url):
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import urllib
|
||||
|
||||
import requests
|
||||
from url_shortener import URLShortener
|
||||
|
||||
class OnjmeShortener (URLShortener):
|
||||
@@ -9,10 +9,9 @@ class OnjmeShortener (URLShortener):
|
||||
|
||||
def _shorten (self, url):
|
||||
answer = url
|
||||
api = urllib.urlopen ("http://onj.me/yourls-api.php?action=shorturl&format=simple&url=" + urllib.quote(url))
|
||||
if api.getcode() == 200:
|
||||
answer = api.read()
|
||||
api.close()
|
||||
api = requests.get ("http://onj.me/yourls-api.php?action=shorturl&format=simple&url=" + urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
||||
|
||||
def created_url (self, url):
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import urllib
|
||||
|
||||
import requests
|
||||
from url_shortener import URLShortener
|
||||
|
||||
class TinyArrowsShortener (URLShortener):
|
||||
@@ -9,8 +9,10 @@ class TinyArrowsShortener (URLShortener):
|
||||
|
||||
def _shorten (self, url):
|
||||
answer = url
|
||||
answer = urllib.urlopen("http://tinyarro.ws/api-create.php?utfpure=1&url=%s" % urllib.quote(url)).read()
|
||||
api = requests.get("http://tinyarro.ws/api-create.php?utfpure=1&url=%s" % urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer.decode('UTF-8')
|
||||
|
||||
def created_url(self, url):
|
||||
return False
|
||||
return "tinyarro.ws" in url
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from url_shortener import URLShortener
|
||||
import requests
|
||||
import urllib
|
||||
class TinyurlShortener (URLShortener):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -6,12 +7,10 @@ class TinyurlShortener (URLShortener):
|
||||
super(TinyurlShortener, self).__init__(*args, **kwargs)
|
||||
|
||||
def _shorten (self, url):
|
||||
|
||||
answer = url
|
||||
api = urllib.urlopen ("http://tinyurl.com/api-create.php?url=" + urllib.quote(url))
|
||||
if api.getcode() == 200:
|
||||
answer = api.read()
|
||||
api.close()
|
||||
api = requests.get ("http://tinyurl.com/api-create.php?url=" + urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
||||
|
||||
def created_url (self, url):
|
||||
|
@@ -1,6 +1,4 @@
|
||||
from httplib import HTTPConnection
|
||||
from urlparse import urlparse
|
||||
|
||||
import requests
|
||||
|
||||
class URLShortener (object):
|
||||
|
||||
@@ -22,12 +20,18 @@ class URLShortener (object):
|
||||
raise NotImplementedError
|
||||
|
||||
def unshorten(self, url):
|
||||
working = urlparse(url)
|
||||
if not working.netloc:
|
||||
raise TypeError, "Unable to parse URL."
|
||||
con = HTTPConnection(working.netloc)
|
||||
con.connect()
|
||||
con.request('GET', working.path)
|
||||
resp = con.getresponse()
|
||||
con.close()
|
||||
return resp.getheader('location')
|
||||
try:
|
||||
r=requests.head(url)
|
||||
if 'location' in r.headers.keys():
|
||||
if 'dropbox.com' in r.headers['location']:
|
||||
return handle_dropbox(r.headers['location'])
|
||||
else:
|
||||
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,5 +1,5 @@
|
||||
import urllib
|
||||
|
||||
import requests
|
||||
from url_shortener import URLShortener
|
||||
|
||||
class XedccShortener (URLShortener):
|
||||
@@ -9,10 +9,9 @@ class XedccShortener (URLShortener):
|
||||
|
||||
def _shorten (self, url):
|
||||
answer = url
|
||||
api = urllib.urlopen ("http://xed.cc/yourls-api.php?action=shorturl&format=simple&url=" + urllib.quote(url))
|
||||
if api.getcode() == 200:
|
||||
answer = api.read()
|
||||
api.close()
|
||||
api = requests.get ("http://xed.cc/yourls-api.php?action=shorturl&format=simple&url=" + urllib.quote(url))
|
||||
if api.status_code == 200:
|
||||
answer = api.text
|
||||
return answer
|
||||
|
||||
def created_url (self, url):
|
||||
|
@@ -26,8 +26,7 @@ class searchDialog(baseDialog.BaseWXDialog):
|
||||
radioSizer.Add(self.users, 0, wx.ALL, 5)
|
||||
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
||||
lang = wx.StaticText(panel, -1, _(u"&Language for results: "))
|
||||
langs = [x[1] for x in translator.translator.available_languages()]
|
||||
langs[:] = langs[1:]
|
||||
langs = [x for x in translator.translator.languages.values()]
|
||||
langs.insert(0, _(u"any"))
|
||||
self.lang = wx.ComboBox(panel, -1, choices=langs, value=langs[0], style = wx.CB_READONLY)
|
||||
langBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
@@ -51,7 +50,12 @@ class searchDialog(baseDialog.BaseWXDialog):
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def get_language(self):
|
||||
return [x[0] for x in translator.translator.available_languages()][self.lang.GetSelection()]
|
||||
l = self.lang.GetStringSelection()
|
||||
if l == _(u"any"):
|
||||
return ""
|
||||
for langcode, langname in translator.translator.languages.iteritems():
|
||||
if langname == l:
|
||||
return langcode
|
||||
|
||||
def get_result_type(self):
|
||||
r = self.resultstype.GetValue()
|
||||
|
Reference in New Issue
Block a user