Compare commits

...

21 Commits

Author SHA1 Message Date
214b9a8809 Fixed a bug with the restart_stream function 2015-12-28 09:05:09 -06:00
e85f54e15c Merge pull request #67 from Oliver2213/update-readme
Update the "upgrade all" command to include missing dependencies
2015-12-27 11:09:59 -06:00
blake oliver
d2245c33ce Update the "upgrade all" command to include missing dependencies 2015-12-27 11:43:25 -05:00
ff6695bba3 Added ANY in the language list for tweet searches 2015-12-27 00:03:15 -06:00
cdcdc86627 Changed go_page_up and go_page_down in the win10 default keymap 2015-12-26 23:39:31 -06:00
b4addf9329 Add update buffer option to the menu bar 2015-12-26 23:34:19 -06:00
9d0c9cfdb5 Don't get new items for people buffers 2015-12-26 23:28:46 -06:00
0afba81c71 Update buffers by pressing ctrl+win+shift+u; workaround for dm issues 2015-12-26 09:02:08 -06:00
0882e4707d Removed an unneeded import in translator module 2015-12-23 09:52:32 -06:00
78079e142f Changed handlers for sent direct messages 2015-12-23 09:51:09 -06:00
00a4203e1a Switched to the microsoft translator 2015-12-23 09:48:50 -06:00
c4fae3b70b Windows key is no longer required in keymap editor 2015-12-17 08:28:20 -06:00
b8dfa4a5e8 added audio playback to the keymap editor 2015-12-16 20:16:18 -06:00
1ca1862a08 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-12-16 16:40:30 -06:00
ba76c74324 Minor changes in session controller 2015-12-16 16:32:58 -06:00
Jose Manuel Delicado
dda37f0083 Updated windows dependencies. Run git submodule update before submiting new commits. Updated readme.md with information about the portableapps.com format. 2015-12-14 13:46:55 +01:00
65c353450e Improvements in the tweets' searched 2015-12-09 17:25:46 -06:00
2bfb53abe1 Added showing followers and friends timelines 2015-12-09 11:51:37 -06:00
ab08eada81 Fixed TWBlue in non-ascii paths 2015-12-09 10:05:17 -06:00
Jose Manuel Delicado
5fcc1de9a5 Updated windows dependencies, remember to run git submodule update after pulling the repository! Removed wheel information about pycurl in readme and updated download links 2015-11-30 17:38:58 +01:00
Jose Manuel Delicado
baeb0f7ae8 Updated pa.c format to version 0.80, added romanian language to the installer 2015-11-30 11:50:59 +01:00
26 changed files with 247 additions and 211 deletions

View File

@@ -1,4 +1,4 @@
TWBlue -
TWBlue -
======
Copyright (C) 2015. [Technow S.L.](https://www.technow.es)
@@ -32,12 +32,11 @@ Although most dependencies can be found in the windows-dependencies directory, w
#### Dependencies packaged in windows installers
* [Python,](http://python.org) version 2.7.10
* [Python,](http://python.org) version 2.7.11
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python27 and python x64 to C:\python27x64, for example.
* [wxPython](http://www.wxpython.org) for Python 2.7, version 3.0.2.0
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 219
* [Pycurl](http://pycurl.sourceforge.net) 7.19.5.1 for Python 2.7: [32-bit downloads,](https://pypi.python.org/pypi/pycurl/7.19.5.1) [64-bit downloads](http://www.lfd.uci.edu/~gohlke/pythonlibs/)
Note: the x64 version is in wheel format instead of executable installer, so you have to install it using pip. For example: C:\python27x64\scripts\pip install pycurl-7.19.5.1-cp27-none-win_amd64.whl
* [Pycurl](http://pycurl.sourceforge.net) 7.19.5.3 for Python 2.7: [downloads](https://pypi.python.org/pypi/pycurl/7.19.5.3)
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
x64 version has been built by TWBlue developers, so you only will find it in windows-dependencies folder
@@ -68,14 +67,14 @@ setuptools install a script, called easy_install. You can find it in the python
* future
* pygeocoder
* suds
* arrow
* goslate
* arrow==0.6
* markdown
* pocket
* winpaths
* microsofttranslator
easy_install will automatically get the additional libraries that these packages need to work properly.
Run the following command to quickly install and upgrade all packages and their dependencies:
easy_install -Z --upgrade six configobj goslate markdown future pocket suds requests oauthlib requests-oauthlib pypubsub pygeocoder arrow python-dateutil futures
easy_install -Z --upgrade six configobj goslate markdown future suds requests oauthlib requests-oauthlib pypubsub pygeocoder arrow python-dateutil futures markdown microsofttranslator
#### Other dependencies
@@ -90,6 +89,11 @@ This dependency has been built using pure basic 4.61. Its source can be found at
* [NSIS unicode,](http://www.scratchpaper.com/) version 2.46.5
#### Dependencies required to build the portableApps.com format archive
* [PortableApps.com Launcher,](http://portableapps.com/apps/development/portableapps.com_launcher) version 2.2
* [PortableApps.com Installer,](http://portableapps.com/apps/development/portableapps.com_installer) version 3.0.20
### Running TW Blue from source
Now that you have installed all these packages, you can run TW Blue from source using a command prompt. Navigate to the repo's src directory, and type the following command:
@@ -129,3 +133,15 @@ If you want to install TWBlue in your computer, you must create the installer fi
### How to generate a translation template
Run the gen_pot.bat file, located in the tools directory. Your python installation must be in your path environment variable. The pot file will appear in the tools directory.
### How to build the portableApps.com archive
If you want to have TWBlue in your PortableApps.com platform, follow these steps:
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
* Move the dist directory to the misc\pa.c format\app folder in this repo, and rename it to twblue
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
* Move the new dist directory to the misc\pa.c format\app folder, and rename it to twblue64
* Run the PortableApps.com Launcher Generator, and follow the wizard. Choose the pa.c format folder and continue to generate the launcher. If the wizard is completed, you will see a file named TWBlue portable.exe inside the pa.c format folder.
* Run the PortableApps.com Installer, and follow the wizard. As in the above step, choose the pa.c format folder. When it completes, you will see a file named TWBluePortable_x.y.paf.exe inside the misc folder, where x.y is the version number.

View File

@@ -3,9 +3,9 @@ Type=PortableApps.comFormat
Version=3.0
[Details]
Name=TWBlue portable
Name=tw blue portable
AppID=TWBluePortable
Publisher=jmdaweb & TWBlue & PortableApps.com
Publisher=jmdaweb & TW blue & PortableApps.com
Homepage=PortableApps.com/TWBluePortable
Category=Internet
Description=A portable, fast and accessible Twitter client with many options.

View File

@@ -8,10 +8,14 @@ FINNISH=true
FRENCH=true
GALICIAN=true
GERMAN=true
CROATIAN=true
HUNGARIAN=true
ITALIAN=true
JAPANESE=true
POLISH=true
PORTUGUESEBR=true
ROMANIAN=true
RUSSIAN=true
SERBIAN=true
SPANISHINTERNATIONAL=true
TURKISH=true
TURKISH=true

View File

@@ -2,7 +2,7 @@
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>TWBlue Portable Help</title>
<title>tw blue Portable Help</title>
<link rel="alternate" href="http://portableapps.com/feeds/general" type="application/rss+xml" title="PortableApps.com">
<link rel="shortcut icon" href="Other/Help/Images/Favicon.ico">
<style type="text/css">
@@ -125,13 +125,13 @@
<body>
<div class="logo"><a href="http://portableapps.com/"><img src="Other/Help/Images/Help_Logo_Top.png" alt="PortableApps.com - Your Digital Life, Anywhere"></a></div>
<div class="content">
<h1 class="hastagline">TWBlue Portable Help</h1>
<h1 class="hastagline">tw blue Portable Help</h1>
<h2 class="tagline">A powerful and accessible Twitter client</h2>
<p>TWBlue Portable is the TWBlue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as TWBlue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about TWBlue...</a></p>
<p>tw blue Portable is the tw blue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as tw blue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about tw blue...</a></p>
<p><a href="http://portableapps.com/donate"><img src="Other/Help/Images/Donation_Button.png" style="vertical-align:middle" alt="Make a Donation"></a> - Support PortableApps.com's Hosting and Development</p>
<p><a href="http://portableapps.com/node/*Node ID*">Go to the TWBlue Portable Homepage &gt;&gt;</a></p>
<p><a href="http://portableapps.com/node/*Node ID*">Go to the tw blue Portable Homepage &gt;&gt;</a></p>
<p><a href="http://portableapps.com/">Get more portable apps at PortableApps.com</a></p>
<p>This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative.</p>

View File

@@ -48,6 +48,7 @@ var StartMenuFolder
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_RESERVEFILE_LANGDLL
Section
SetShellVarContext All

View File

@@ -28,6 +28,8 @@ timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
followers_timelines = list(default=list())
friends_timelines = list(default=list())
trending_topic_buffers = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list(mentions, direct_messages, events))

View File

@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
name = 'TWBlue'
snapshot = False
snapshot = True
if snapshot == False:
version = "0.80"
update_url = 'http://twblue.es/updates/twblue_ngen.json'
else:
version = "10.97"
version = "10.98"
update_url = 'http://twblue.es/updates/snapshots_ngen.json'
author = u"Manuel Cortéz"
authorEmail = "manuel@manuelcortez.net"

View File

@@ -84,7 +84,9 @@ class bufferController(object):
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
self.session.sound.play("volume_changed.ogg")
def start_stream(self):
def start_stream(self, mandatory=False):
if mandatory == True:
output.speak(_(u"Unable to update this buffer."))
pass
def get_more_items(self):
@@ -272,10 +274,10 @@ class baseBufferController(bufferController):
tweetsList.append(tweet)
return (tweet, tweetsList)
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
@@ -283,9 +285,9 @@ class baseBufferController(bufferController):
number_of_items = self.session.order_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if self.sound == None: return
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages":
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None:
self.session.sound.play(self.sound)
return number_of_items
def get_more_items(self):
elements = []
@@ -481,7 +483,13 @@ class baseBufferController(bufferController):
users = utils.get_all_users(tweet, self.session.db)
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
if dm.message.get_response() == widgetUtils.OK:
call_threaded(self.session.api_call, call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
val = self.session.api_call(call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
if val != None:
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db["sent_direct_messages"].append(val)
else:
self.session.db["sent_direct_messages"].insert(0, val)
pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"])
if hasattr(dm.message, "destroy"): dm.message.destroy()
@_tweets_exist
@@ -638,9 +646,9 @@ class listBufferController(baseBufferController):
self.list_id = list_id
self.kwargs["list_id"] = list_id
def start_stream(self):
def start_stream(self, mandatory=False):
self.get_user_ids()
super(listBufferController, self).start_stream()
super(listBufferController, self).start_stream(mandatory)
def get_user_ids(self):
self.users = []
@@ -714,7 +722,7 @@ class eventsBufferController(bufferController):
class peopleBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel")
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.compose_function = compose.compose_followers_list
log.debug("Compose_function: %s" % (self.compose_function,))
@@ -722,7 +730,25 @@ class peopleBufferController(baseBufferController):
self.url = self.interact
def remove_buffer(self):
return False
if "-followers" in self.name:
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
if self.name[:-10] in self.session.settings["other_buffers"]["followers_timelines"]:
self.session.settings["other_buffers"]["followers_timelines"].remove(self.name[:-10])
return True
elif dlg == widgetUtils.NO:
return False
elif "-friends" in self.name:
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
if self.name[:-8] in self.session.settings["other_buffers"]["friends_timelines"]:
self.session.settings["other_buffers"]["friends_timelines"].remove(self.name[:-9])
return True
elif dlg == widgetUtils.NO:
return False
else:
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
return False
def onFocus(self, ev):
pass
@@ -744,15 +770,16 @@ class peopleBufferController(baseBufferController):
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):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.get_cursored_stream(self.name, self.function, *self.args, **self.kwargs)
self.put_items_on_list(val)
return val
def get_more_items(self):
try:
@@ -841,10 +868,10 @@ class peopleBufferController(baseBufferController):
pub.sendMessage("execute-action", action="user_details")
class searchBufferController(baseBufferController):
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
@@ -857,6 +884,7 @@ class searchBufferController(baseBufferController):
self.put_items_on_list(num)
if num > 0:
self.session.sound.play("search_updated.ogg")
return num
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
@@ -879,10 +907,10 @@ class searchPeopleBufferController(peopleBufferController):
self.kwargs = kwargs
self.function = function
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
@@ -896,6 +924,7 @@ class searchPeopleBufferController(peopleBufferController):
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
return number_of_items
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
@@ -1052,6 +1081,7 @@ class conversationBufferController(searchBufferController):
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
return number_of_items
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()

View File

@@ -118,6 +118,7 @@ class Controller(object):
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
pub.subscribe(self.manage_item_in_timeline, "item-in-timeline")
pub.subscribe(self.manage_item_in_list, "item-in-list")
pub.subscribe(self.restart_streams_, "restart_streams")
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
def bind_other_events(self):
@@ -179,6 +180,7 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
def set_systray_icon(self):
self.systrayIcon = sysTrayIcon.SysTrayIcon()
@@ -344,6 +346,24 @@ class Controller(object):
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
followers_timelines = buffersController.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
self.buffers.append(followers_timelines)
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["followers_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (i,), session, session.db["user_name"], screen_name=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
friends_timelines = buffersController.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
self.buffers.append(friends_timelines)
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["friends_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (i,), session, session.db["user_name"], screen_name=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
lists = buffersController.emptyPanel(self.view.nb, "lists", session.db["user_name"])
self.buffers.append(lists)
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
@@ -410,13 +430,14 @@ class Controller(object):
if dlg.get("tweets") == True:
if term not in buffer.session.settings["other_buffers"]["tweet_searches"]:
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", q=term, count=buffer.session.settings["general"]["max_tweets_per_call"])
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", q=term, count=buffer.session.settings["general"]["max_tweets_per_call"], **args)
else:
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
return
elif dlg.get("users") == True:
search = buffersController.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, q=term)
search.start_stream()
search.start_stream(mandatory=True)
pos=self.view.search("searches", buffer.session.db["user_name"])
self.insert_buffer(search, pos)
self.view.insert_buffer(search.buffer, name=_(u"Search for {}").format(term), pos=pos)
@@ -759,7 +780,8 @@ class Controller(object):
return
answer = commonMessageDialogs.protected_user()
if answer == widgetUtils.NO: return
if dlg.get_action() == "tweets":
tl_type = dlg.get_action()
if tl_type == "tweets":
if usr["statuses_count"] == 0:
commonMessageDialogs.no_tweets()
return
@@ -775,7 +797,7 @@ class Controller(object):
buff.session.settings["other_buffers"]["timelines"].append(dlg.get_user())
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
buff.session.sound.play("create_timeline.ogg")
else:
elif tl_type == "favourites":
if usr["favourites_count"] == 0:
commonMessageDialogs.no_favs()
return
@@ -792,6 +814,40 @@ class Controller(object):
tl.timer.start()
buff.session.settings["other_buffers"]["favourites_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "followers":
if usr["followers_count"] == 0:
commonMessageDialogs.no_followers()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["followers_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], screen_name=dlg.get_user())
pos=self.view.search("followers_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Followers for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buff.session.settings["other_buffers"]["followers_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "friends":
if usr["friends_count"] == 0:
commonMessageDialogs.no_friends()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["friends_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], screen_name=dlg.get_user())
pos=self.view.search("friends_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Friends for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buff.session.settings["other_buffers"]["friends_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
else:
commonMessageDialogs.user_not_exist()
@@ -1405,5 +1461,21 @@ class Controller(object):
if hasattr(self, action):
getattr(self, action)()
def restart_streams_(self, session):
for i in self.buffers:
if i.session != None and i.session.session_id == session:
i.start_stream()
def __del__(self):
config.app.write()
config.app.write()
def update_buffer(self, *args, **kwargs):
bf = self.get_current_buffer()
if not hasattr(bf, "start_stream"):
output.speak(_(u"Unable to update this buffer."))
return
else:
output.speak(_(u"Updating buffer..."))
n = bf.start_stream(mandatory=True)
if n != None:
output.speak(_(u"{0} items retrieved").format(n,))

View File

@@ -35,7 +35,7 @@ class basicTweet(object):
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.get_text().encode("utf-8")
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)

View File

@@ -1,153 +1,10 @@
# encoding: utf-8
#
# Copyright (C) 2013 Mesar Hameed <mhameed@src.gnome.org>
# This file is covered by the GNU General Public License.
# -*- coding: utf-8 -*-
from microsofttranslator import Translator
import os
import re
import sys
import threading
from time import sleep
from random import randint
import logging
log = logging.getLogger("translator")
import urllib2
def translate(text="", source="auto", target="en"):
t = Translator("twblue", "4KZA26GYIfmVAqQA/z16Hlucbg64hVSDTIpRjT2FqIU=")
return t.translate(text, target)
# Each group has to be a class of possible breaking points for the writing script.
# Usually this is the major syntax marks, such as:
# full stop, comma, exclaim, question, etc.
arabicBreaks = u'[،؛؟]'
# Thanks to Talori in the NVDA irc room:
# U+3000 to U+303F, U+FE10 to U+FE1F, U+FE30 to U+FE6F, U+FF01 to U+FF60
chineseBreaks = u'[ -〿︐-︟︰-﹯!-⦆]'
latinBreaks = r'[.,!?;:\n]'
splitReg = re.compile(u"{arabic}|{chinese}|{latin}".format(arabic=arabicBreaks, chinese=chineseBreaks, latin=latinBreaks))
def translate(text, source="auto", target="en"):
if source == "": source = "auto"
t = Translator(lang_from=source, lang_to=target, text=text)
t.start()
while t.isAlive():
sleep(0.1)
t.join()
return t.translation
def splitChunks(text, chunksize):
pos = 0
potentialPos = 0
for splitMark in splitReg.finditer(text):
if (splitMark.start() - pos +1) < chunksize:
potentialPos = splitMark.start()
continue
else:
yield text[pos:potentialPos+1]
pos = potentialPos + 1
potentialPos = splitMark.start()
yield text[pos:]
class Translator(threading.Thread):
def __init__(self, lang_from, lang_to, text, lang_swap=None, chunksize=350, *args, **kwargs):
super(Translator, self).__init__(*args, **kwargs)
self._stop = threading.Event()
self.text = text
self.chunksize = chunksize
self.lang_to = lang_to
self.lang_from = lang_from
self.lang_swap = lang_swap
self.translation = ''
self.lang_translated = ''
self.firstChunk = True
def stop(self):
self._stop.set()
def run(self):
for chunk in splitChunks(self.text, self.chunksize):
# Make sure we don't send requests to google too often.
# Try to simulate a human.
if not self.firstChunk:
sleep(randint(1, 10))
req = self.buildRequest(chunk, self.lang_from, self.lang_to)
try:
response = urllib2.urlopen(req)
translation, lang_translated = self.parseData(response)
if self.firstChunk and self.lang_from == "auto" and lang_translated == self.lang_to and self.lang_swap is not None:
self.lang_to = self.lang_swap
self.firstChunk = False
req = self.buildRequest(chunk.encode('utf-8'), self.lang_from, self.lang_to)
response = urllib2.urlopen(req)
translation, lang_translated = self.parseData(response)
except Exception as e:
log.exception("Can not translate text '%s'" %chunk)
# We have probably been blocked, so stop trying to translate.
raise e
self.translation += translation
# some adjustment, better to do on full text
self.translation = self.fixNewlines(self.translation)
self.lang_translated = lang_translated
def buildRequest(self, text, lang_from, lang_to):
"""Build POST request which will be sent to Google."""
urlTemplate = 'http://translate.google.com/translate_a/single?client=t&sl={lang_from}&tl={lang_to}&ie=utf-8&oe=utf-8&dt=t&dt=bd&tk='
url = urlTemplate.format(lang_from=lang_from, lang_to=lang_to)
header = {'User-agent': 'Mozilla/5.0', 'Content-Type': 'application/x-www-form-urlencoded'}
data = 'text=%s' %urllib2.quote(text)
req = urllib2.Request(url, data, header)
return req
def parseData(self, response):
"""Parse unstructured response."""
data = response.readlines()[0]
# get segments with couples ["translation","original text"]
l1, l2 = data.split(']],', 1)
translation = l1[3:]
if l2.startswith('[[\"'):
# get list of synonyms
syn = l2[l2.find(',[')+1:l2.find(']')].split(',')
temp = ', '.join([x.replace('\"', '') for x in syn])
else:
# get a list with each couple as item
sentences = translation.split('],[')
temp = ''
# get translation, removing first char (quote symbol)
for item in sentences:
item = item.split('\",\"', 1)[0][1:]
# join all translations
temp = ' '.join([temp, item])
translation = temp.decode('string-escape').decode('utf-8')
translation = self.fixPunctuation(translation)
# get the language of original text
tempLang = data.partition(']],,\"')[2]
lang = tempLang[:tempLang.find('\"')]
if lang == '':
lang = _("unavailable")
return translation, lang
def fixPunctuation(self, translation):
"""Clean text from space before punctuation symbol."""
# list of potentially positions of spaces to remove
spacePos = []
for puncMark in splitReg.finditer(translation):
spacePos.append(puncMark.start()-1)
if len(spacePos) == 0:
return translation
fixedTranslation = ''
for n in xrange(0,len(translation)):
temp = translation[n]
if n in spacePos and temp == ' ':
continue
else:
fixedTranslation += temp
return fixedTranslation
def fixNewlines(self, translation):
"""Adjust newlines and (subsequent or double) spaces."""
fixes = [('\r\n ', '\r\n'), ('\n ', '\r\n'), (' ', ' ')]
for fix in fixes:
translation = translation.replace(fix[0], fix[1])
# first char is a space, so...
return translation[1:]
languages = {
"af": _(u"Afrikaans"),

View File

@@ -31,4 +31,5 @@ find = string(default="control+win+shift+/")
check_for_updates = string(default="alt+win+u)
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -50,4 +50,5 @@ get_trending_topics = string(default="control+win+shift+t")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -30,8 +30,8 @@ volume_up = string(default="alt+win+shift+up")
go_home = string(default="alt+win+home")
volume_down = string(default="alt+win+shift+down")
go_end = string(default="alt+win+end")
go_page_up = string(default="alt+win+pageup")
go_page_down = string(default="alt+win+pagedown")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="alt+win+delete")
clear_buffer = string(default="alt+win+shift+delete")
@@ -52,4 +52,5 @@ get_trending_topics = string(default="alt+win+t")
check_for_updates = string(default="alt+win+u")
list_manager = string(default="alt+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+alt+shift+u")

View File

@@ -53,4 +53,5 @@ get_trending_topics = string(default="control+win+t")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -53,4 +53,5 @@ find = string(default="control+win+{")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -50,4 +50,6 @@ actions = {
"lists_manager": _(u"Opens the list manager, which allows you to create, edit, delete and open lists in buffers."),
"configuration": _(u"Opens the global settings dialogue"),
"accountConfiguration": _(u"Opens the account settings dialogue"),
"audio": _(u"Try to play an audio file"),
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
}

View File

@@ -37,13 +37,10 @@ class KeystrokeEditor(object):
def get_edited_keystroke(self, dialog):
keys = []
if dialog.get("win") == False:
wx_ui.no_win_message()
return
if dialog.get("control") == True:
keys.append("control")
# if dialog.get("win") == True:
keys.append("win")
if dialog.get("win") == True:
keys.append("win")
if dialog.get("alt") == True:
keys.append("alt")
if dialog.get("shift") == True:

View File

@@ -55,7 +55,8 @@ def data_path(app_name='TW blue'):
# data_path = os.path.join(shlobj.SHGetFolderPath(0, shlobj.CSIDL_APPDATA), app_name)
# else:
if platform.system() == "Windows":
data_path = os.path.join(os.getenv("AppData"), app_name)
import winpaths
data_path = os.path.join(winpaths.get_appdata(), app_name)
else:
data_path = os.path.join(os.environ['HOME'], ".%s" % app_name)
if not os.path.exists(data_path):

View File

@@ -194,6 +194,7 @@ class Session(object):
if report_success:
output.speak(_("%s succeeded.") % action)
if _sound != None: self.sound.play(_sound)
return val
def search(self, name, *args, **kwargs):
tl = self.twitter.twitter.search(*args, **kwargs)
@@ -278,8 +279,7 @@ class Session(object):
tl = self.call_paged(function, sinze_id=last_id, *args, **kwargs)
self.order_buffer(name, tl)
@_require_login
def get_cursored_stream(self, name, function, items="users", *args, **kwargs):
def get_cursored_stream(self, name, function, items="users", get_previous=False, *args, **kwargs):
""" Gets items for API calls that require using cursors to paginate the results.
name str: Name to save it in the database.
@@ -289,7 +289,7 @@ class Session(object):
items_ = []
try:
if self.db[name].has_key("cursor"):
if self.db[name].has_key("cursor") and get_previous:
cursor = self.db[name]["cursor"]
else:
cursor = -1
@@ -352,7 +352,7 @@ class Session(object):
self.logged = False
self.twitter = twitter.twitter.twitter()
self.login(False)
# pub.sendMessage("streamError", session=self.session_id)
pub.sendMessage("restart_streams", session=self.session_id)
if self.reconnection_function_active == True: return
self.reconnection_function_active = True
if not hasattr(self, "main_stream"):

View File

@@ -87,11 +87,11 @@ class streamer(TwythonStreamer):
pub.sendMessage("mention", data=data, user=self.get_user())
def process_dm(self, data):
if self.session.db["user_name"] == data["direct_message"]["sender"]["screen_name"]:
d = self.put_data("sent_direct_messages", data["direct_message"])
if d != False:
pub.sendMessage("sent-dm", data=data["direct_message"], user=self.get_user())
else:
if self.session.db["user_name"] != data["direct_message"]["sender"]["screen_name"]:
# d = self.put_data("sent_direct_messages", data["direct_message"])
# if d != False:
# pub.sendMessage("sent-dm", data=data["direct_message"], user=self.get_user())
# else:
d = self.put_data("direct_messages", data["direct_message"])
if d != False:
pub.sendMessage("direct-message", data=data["direct_message"], user=self.get_user())

View File

@@ -58,3 +58,9 @@ def no_tweets():
def no_favs():
return wx.MessageDialog(None, _(u"This user has no favorited tweets. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
def no_followers():
return wx.MessageDialog(None, _(u"This user has no followers. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
def no_friends():
return wx.MessageDialog(None, _(u"This user has no friends. {0} can't create a timeline.").format(application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()

View File

@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import widgetUtils
import baseDialog
import wx
from extra import translator
class searchDialog(baseDialog.BaseWXDialog):
def __init__(self, value=""):
@@ -17,10 +19,27 @@ class searchDialog(baseDialog.BaseWXDialog):
sizer.Add(self.term, 0, wx.ALL, 5)
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
self.users = wx.RadioButton(panel, -1, _(u"Users"))
widgetUtils.connect_event(self.tweets, widgetUtils.RADIOBUTTON, self.show_advanced_search)
widgetUtils.connect_event(self.users, widgetUtils.RADIOBUTTON, self.hide_advanced_search)
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
radioSizer.Add(self.tweets, 0, wx.ALL, 5)
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.insert(0, _(u"any"))
self.lang = wx.ComboBox(panel, -1, choices=langs, value=langs[0], style = wx.CB_READONLY)
langBox = wx.BoxSizer(wx.HORIZONTAL)
langBox.Add(lang, 0, wx.ALL, 5)
langBox.Add(self.lang, 0, wx.ALL, 5)
sizer.Add(langBox, 0, wx.ALL, 5)
resulttype = wx.StaticText(panel, -1, _(U"Results type: "))
self.resultstype = wx.ComboBox(panel, -1, choices=[_(u"Mixed"), _(u"Recent"), _(u"Popular")], value=_(u"Mixed"), style=wx.CB_READONLY)
rBox = wx.BoxSizer(wx.HORIZONTAL)
rBox.Add(resulttype, 0, wx.ALL, 5)
rBox.Add(self.resultstype, 0, wx.ALL, 5)
sizer.Add(rBox, 0, wx.ALL, 5)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
@@ -29,4 +48,21 @@ class searchDialog(baseDialog.BaseWXDialog):
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
self.SetClientSize(sizer.CalcMin())
def get_language(self):
return [x[0] for x in translator.translator.available_languages()][self.lang.GetSelection()]
def get_result_type(self):
r = self.resultstype.GetValue()
if r == _(u"Mixed"): return "mixed"
elif r == _(u"Recent"): return "recent"
elif r == _(u"Popular"): return "popular"
def hide_advanced_search(self, *args, **kwargs):
self.lang.Hide()
self.resultstype.Hide()
def show_advanced_search(self, *args, **kwargs):
self.lang.Show()
self.resultstype.Show()

View File

@@ -18,11 +18,15 @@ class selectUserDialog(wx.Dialog):
label2 = wx.StaticText(panel, -1, _(u"Buffer type"))
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
self.favourites = wx.RadioButton(panel, -1, _(u"Likes"))
self.followers = wx.RadioButton(panel, -1, _(u"Followers"))
self.friends = wx.RadioButton(panel, -1, _(u"Friends"))
self.setup_default(default)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
hSizer.Add(label2, 0, wx.ALL, 5)
actionSizer.Add(self.tweets, 0, wx.ALL, 5)
actionSizer.Add(self.favourites, 0, wx.ALL, 5)
actionSizer.Add(self.followers, 0, wx.ALL, 5)
actionSizer.Add(self.friends, 0, wx.ALL, 5)
hSizer.Add(actionSizer, 0, wx.ALL, 5)
sizer = wx.BoxSizer(wx.VERTICAL)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
@@ -39,6 +43,8 @@ class selectUserDialog(wx.Dialog):
def get_action(self):
if self.tweets.GetValue() == True: return "tweets"
elif self.favourites.GetValue() == True: return "favourites"
elif self.followers.GetValue() == True: return "followers"
elif self.friends.GetValue() == True: return "friends"
def setup_default(self, default):
if default == "tweets":

View File

@@ -47,6 +47,7 @@ class mainFrame(wx.Frame):
# buffer menu
buffer = wx.Menu()
self.update_buffer = buffer.Append(wx.NewId(), _(u"&Update buffer"))
self.trends = buffer.Append(wx.NewId(), _(u"New &trending topics buffer..."))
self.find = buffer.Append(wx.NewId(), _(u"Find a string in the currently focused buffer..."))
self.load_previous_items = buffer.Append(wx.NewId(), _(u"&Load previous items"))