Added some code for starting
This commit is contained in:
		
							
								
								
									
										29
									
								
								src/accessible_output2/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/accessible_output2/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import ctypes | ||||
| import os | ||||
| import types | ||||
| from platform_utils import paths | ||||
|  | ||||
| def load_library(libname): | ||||
|  if paths.is_frozen(): | ||||
|   libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname) | ||||
|  else: | ||||
|   libfile = os.path.join(paths.module_path(), 'lib', libname) | ||||
|  return ctypes.windll[libfile] | ||||
|  | ||||
| def get_output_classes(): | ||||
|  import outputs | ||||
|  module_type = types.ModuleType | ||||
|  classes = [m.output_class for m in outputs.__dict__.itervalues() if type(m) == module_type and hasattr(m, 'output_class')] | ||||
|  return sorted(classes, key=lambda c: c.priority) | ||||
|  | ||||
| def find_datafiles(): | ||||
|  import os | ||||
|  import platform | ||||
|  from glob import glob | ||||
|  import accessible_output2 | ||||
|  if platform.system() != 'Windows': | ||||
|   return [] | ||||
|  path = os.path.join(accessible_output2.__path__[0], 'lib', '*.dll') | ||||
|  results = glob(path) | ||||
|  dest_dir = os.path.join('accessible_output2', 'lib') | ||||
|  return [(dest_dir, results)] | ||||
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/PCTKUSR.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/PCTKUSR.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/PCTKUSR64.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/PCTKUSR64.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/SAAPI32.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/SAAPI32.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/dolapi.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/dolapi.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/jfwapi.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/jfwapi.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/nvdaControllerClient32.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/nvdaControllerClient32.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/accessible_output2/lib/nvdaControllerClient64.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/accessible_output2/lib/nvdaControllerClient64.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										14
									
								
								src/accessible_output2/outputs/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/accessible_output2/outputs/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| import platform | ||||
| if platform.system() == 'Windows': | ||||
|  import nvda | ||||
|  import jaws | ||||
|  import window_eyes | ||||
|  import system_access | ||||
|  import dolphin | ||||
|  import pc_talker | ||||
| elif platform.system() == "Darwin": | ||||
|  import voiceover | ||||
| elif platform.system() == "Linux": | ||||
|  import speechDispatcher | ||||
|  | ||||
| import auto | ||||
							
								
								
									
										42
									
								
								src/accessible_output2/outputs/auto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/accessible_output2/outputs/auto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| import platform | ||||
| import accessible_output2 | ||||
| from base import Output, OutputError | ||||
|  | ||||
| class Auto(Output): | ||||
|  | ||||
|  def __init__(self): | ||||
|   if platform.system() == "Darwin": | ||||
|    import voiceover | ||||
|    self.outputs = [voiceover.VoiceOver()] | ||||
|   elif platform.system() == "Linux": | ||||
|    import speechDispatcher | ||||
|    self.outputs = [speechDispatcher.SpeechDispatcher()] | ||||
|   elif platform.system() == "Windows": | ||||
|    output_classes = accessible_output2.get_output_classes() | ||||
|    self.outputs = [] | ||||
|    for output in output_classes: | ||||
|     try: | ||||
|      self.outputs.append(output()) | ||||
|     except OutputError: | ||||
|      pass | ||||
|  | ||||
|  def get_first_available_output(self): | ||||
|   for output in self.outputs: | ||||
|    if output.is_active(): | ||||
|     return output | ||||
|   return None | ||||
|  | ||||
|  def speak(self, *args, **kwargs): | ||||
|   output = self.get_first_available_output() | ||||
|   if output: | ||||
|    output.speak(*args, **kwargs) | ||||
|  | ||||
|  def braille(self, *args, **kwargs): | ||||
|   output = self.get_first_available_output() | ||||
|   if output: | ||||
|    output.braille(*args, **kwargs) | ||||
|  | ||||
|  def output(self, *args, **kwargs): | ||||
|   output = self.get_first_available_output() | ||||
|   if output: | ||||
|    output.speak(*args, **kwargs) | ||||
							
								
								
									
										31
									
								
								src/accessible_output2/outputs/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/accessible_output2/outputs/base.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| from accessible_output2 import load_library | ||||
| import platform | ||||
|  | ||||
| class OutputError(Exception): | ||||
|  pass | ||||
|  | ||||
| class Output(object): | ||||
|  name = "Unnamed Output" #The name of this output | ||||
|  lib32 = None #name of 32-bit lib | ||||
|  lib64 = None #name of 64-bit lib | ||||
|  priority = 100 #Where to sort in the list of available outputs for automaticly speaking | ||||
|  | ||||
|  def __init__(self): | ||||
|   is_32bit = platform.architecture()[0] == "32bit" | ||||
|   if self.lib32 and is_32bit: | ||||
|    self.lib = load_library(self.lib32) | ||||
|   elif self.lib64: | ||||
|    self.lib = load_library(self.lib64) | ||||
|  | ||||
|  def output(self, text, **options): | ||||
|   output = False | ||||
|   if hasattr(self, 'speak') and callable(self.speak): | ||||
|    self.speak(text, **options) | ||||
|    output = True | ||||
|   if hasattr(self, 'braille') and callable(self.braille): | ||||
|    self.braille(text, **options) | ||||
|    output = True | ||||
|   if not output: | ||||
|    raise RuntimeError("Output %r does not have any method defined to output" % self) | ||||
|  | ||||
|  | ||||
							
								
								
									
										27
									
								
								src/accessible_output2/outputs/dolphin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/accessible_output2/outputs/dolphin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| import os | ||||
|  | ||||
| from base import Output | ||||
|  | ||||
| class Dolphin (Output): | ||||
|  """Supports dolphin products.""" | ||||
|  | ||||
|  name = 'Dolphin' | ||||
|  lib32 = 'dolapi.dll' | ||||
|  | ||||
|  def speak(self, text, interrupt=0): | ||||
|   if interrupt: | ||||
|    self.silence() | ||||
|   #If we don't call this, the API won't let us speak. | ||||
|   if self.is_active(): | ||||
|    self.lib.DolAccess_Command(unicode(text), (len(text)*2)+2, 1) | ||||
|  | ||||
|  def silence(self): | ||||
|   self.lib.DolAccess_Action(141) | ||||
|  | ||||
|  def is_active(self): | ||||
|   try: | ||||
|    return self.lib.DolAccess_GetSystem() in (1, 4, 8) | ||||
|   except: | ||||
|    return False | ||||
|  | ||||
| output_class = Dolphin | ||||
							
								
								
									
										33
									
								
								src/accessible_output2/outputs/jaws.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/accessible_output2/outputs/jaws.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import win32gui | ||||
| from libloader.com import load_com | ||||
| import pywintypes | ||||
|  | ||||
| from base import Output, OutputError | ||||
|  | ||||
| class Jaws (Output): | ||||
|  """Output supporting the Jaws for Windows screen reader.""" | ||||
|  | ||||
|  name = 'jaws' | ||||
|  | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super (Jaws, self).__init__(*args, **kwargs) | ||||
|   try: | ||||
|    self.object = load_com("FreedomSci.JawsApi", "jfwapi") | ||||
|   except pywintypes.com_error: | ||||
|    raise OutputError | ||||
|  | ||||
|  def braille(self, text, **options): | ||||
|   # HACK: replace " with ', Jaws doesn't seem to understand escaping them with \ | ||||
|   text = text.replace('"', "'") | ||||
|   self.object.RunFunction("BrailleString(\"%s\")" % text) | ||||
|  | ||||
|  def speak(self, text, interrupt=False): | ||||
|   self.object.SayString('      %s' % text, interrupt) | ||||
|  | ||||
|  def is_active(self): | ||||
|   try: | ||||
|    return self.object.SayString('',0) == True or win32gui.FindWindow("JFWUI2", "JAWS") != 0 | ||||
|   except: | ||||
|    return False | ||||
|  | ||||
| output_class = Jaws | ||||
							
								
								
									
										31
									
								
								src/accessible_output2/outputs/nvda.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/accessible_output2/outputs/nvda.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import os | ||||
| import platform | ||||
|  | ||||
| from platform_utils import paths | ||||
| from libloader import load_library | ||||
| from base import Output | ||||
|  | ||||
| class NVDA(Output): | ||||
|  """Supports The NVDA screen reader""" | ||||
|  name = "NVDA" | ||||
|  lib32 = 'nvdaControllerClient32.dll' | ||||
|  lib64 = 'nvdaControllerClient64.dll' | ||||
|  | ||||
|  def is_active(self): | ||||
|   try: | ||||
|    return self.lib.nvdaController_testIfRunning() == 0 | ||||
|   except: | ||||
|    return False | ||||
|  | ||||
|  def braille(self, text, **options): | ||||
|   self.lib.nvdaController_brailleMessage(unicode(text)) | ||||
|  | ||||
|  def speak(self, text, interrupt=False): | ||||
|   if interrupt: | ||||
|    self.silence() | ||||
|   self.lib.nvdaController_speakText(unicode(text)) | ||||
|  | ||||
|  def silence(self): | ||||
|   self.lib.nvdaController_cancelSpeech() | ||||
|  | ||||
| output_class = NVDA | ||||
							
								
								
									
										19
									
								
								src/accessible_output2/outputs/pc_talker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/accessible_output2/outputs/pc_talker.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import ctypes | ||||
| from base import Output | ||||
|  | ||||
| class PCTalker(Output): | ||||
|  lib32 = 'pctkusr.dll' | ||||
|  lib64 = 'pctkusr64.dll' | ||||
|  | ||||
|  def speak(self, text, interrupt=False): | ||||
|   if interrupt: | ||||
|    self.silence() | ||||
|   self.lib.PCTKPRead(text.encode('cp932', 'replace')) | ||||
|  | ||||
|  def silence(self): | ||||
|   self.lib.PCTKVReset() | ||||
|  | ||||
|  def is_active(self): | ||||
|   return self.lib.PCTKStatus() != 0 | ||||
|  | ||||
| output_class = PCTalker | ||||
							
								
								
									
										29
									
								
								src/accessible_output2/outputs/speechDispatcher.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/accessible_output2/outputs/speechDispatcher.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| from base import Output, OutputError | ||||
| import atexit | ||||
| import application | ||||
| class SpeechDispatcher(Output): | ||||
|  """Supports speech dispatcher on Linux. | ||||
|  Note that this module will use the configuration of speech dispatcher, the user will need to configure the voice, language, punctuation and rate before using this module. | ||||
|  """ | ||||
|  name = 'SpeechDispatcher' | ||||
|  | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super(SpeechDispatcher, self).__init__(*args, **kwargs) | ||||
|   try: | ||||
|    import speechd | ||||
|    self.spd = speechd.SSIPClient(application.name) | ||||
|   except ImportError: | ||||
|    raise OutputError | ||||
|   atexit.register(self.on_exit_event) | ||||
|  | ||||
|  def speak(self, text, interupt=False): | ||||
|   if interupt == True: | ||||
|    self.spd.cancel() | ||||
|   self.spd.speak(text) | ||||
|  | ||||
|  def is_active(self): | ||||
|   return True | ||||
|  | ||||
|  def on_exit_event(self): | ||||
|   self.spd.close() | ||||
|   del self.spd | ||||
							
								
								
									
										18
									
								
								src/accessible_output2/outputs/speechd/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/accessible_output2/outputs/speechd/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # Copyright (C) 2001, 2002 Brailcom, o.p.s. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  | ||||
| from .client import * | ||||
|  | ||||
							
								
								
									
										1125
									
								
								src/accessible_output2/outputs/speechd/client.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1125
									
								
								src/accessible_output2/outputs/speechd/client.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								src/accessible_output2/outputs/speechd/paths.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/accessible_output2/outputs/speechd/paths.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| SPD_SPAWN_CMD = "/usr/bin/speech-dispatcher" | ||||
							
								
								
									
										23
									
								
								src/accessible_output2/outputs/system_access.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/accessible_output2/outputs/system_access.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| from base import Output | ||||
|  | ||||
| class SystemAccess (Output): | ||||
|  """Supports System Access and System Access Mobile""" | ||||
|  | ||||
|  name = "System Access" | ||||
|  lib32 = 'saapi32.dll' | ||||
|  priority = 99 | ||||
|  | ||||
|  def braille(self, text, **options): | ||||
|   self.lib.SA_BrlShowTextW(unicode(text)) | ||||
|  | ||||
|  def speak(self, text, interrupt=False): | ||||
|   if self.is_active(): | ||||
|    self.dll.SA_SayW(unicode(text)) | ||||
|  | ||||
|  def is_active(self): | ||||
|   try: | ||||
|    return self.dll.SA_IsRunning() | ||||
|   except: | ||||
|    return False | ||||
|  | ||||
| output_class = SystemAccess | ||||
							
								
								
									
										23
									
								
								src/accessible_output2/outputs/voiceover.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/accessible_output2/outputs/voiceover.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| from base import Output, OutputError | ||||
|  | ||||
| class VoiceOver (Output): | ||||
|  """Supports the VoiceOver screenreader on the Mac. | ||||
|  | ||||
|  Note that this will also output as a message to the braille display if VoiceOver is used with braille. | ||||
|  Calling this module could cause VoiceOver to be started. | ||||
|  """ | ||||
|  name = 'VoiceOver' | ||||
|  | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super(VoiceOver, self).__init__(*args, **kwargs) | ||||
|   try: | ||||
|    from appscript import app | ||||
|    self.app = app('VoiceOver') | ||||
|   except ImportError: | ||||
|    raise OutputError | ||||
|  | ||||
|  def speak(self, text, interupt=False): | ||||
|   self.app.output(text) | ||||
|  | ||||
|  def is_active(self): | ||||
|   return True | ||||
							
								
								
									
										32
									
								
								src/accessible_output2/outputs/window_eyes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/accessible_output2/outputs/window_eyes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import win32gui | ||||
| from libloader.com import load_com | ||||
| from base import Output, OutputError | ||||
| import pywintypes | ||||
|  | ||||
| class WindowEyes (Output): | ||||
|  """Speech output supporting the WindowEyes screen reader""" | ||||
|  | ||||
|  name = 'Window-Eyes' | ||||
|  | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super(WindowEyes, self).__init__(*args, **kwargs) | ||||
|   try: | ||||
|    self.object = load_com("gwspeak.speak") | ||||
|   except pywintypes.com_error: | ||||
|    raise OutputError | ||||
|  | ||||
|  def speak(self, text, interrupt=0): | ||||
|   if interrupt: | ||||
|    self.silence() | ||||
|   self.object.SpeakString(text) | ||||
|  | ||||
|  def silence (self): | ||||
|   self.object.Silence() | ||||
|  | ||||
|  def is_active(self): | ||||
|   try: | ||||
|    return win32gui.FindWindow("GWMExternalControl", "External Control") != 0 | ||||
|   except: | ||||
|    return False | ||||
|  | ||||
| output_class = WindowEyes  | ||||
							
								
								
									
										4
									
								
								src/app-configuration.defaults
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app-configuration.defaults
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| [app-settings] | ||||
| username = string(default="") | ||||
| password = string(default="") | ||||
| language = string(default="system") | ||||
							
								
								
									
										16
									
								
								src/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # -*- coding: cp1252 -*- | ||||
| import config_utils | ||||
| import paths | ||||
| import logging | ||||
|  | ||||
| log = logging.getLogger("config") | ||||
|  | ||||
| MAINFILE = "socializer.conf" | ||||
| MAINSPEC = "app-configuration.defaults" | ||||
|  | ||||
| app = None | ||||
| def setup (): | ||||
|  global app | ||||
|  log.debug("Loading global app settings...") | ||||
|  app = config_utils.load_config(paths.config_path(MAINFILE), paths.app_path(MAINSPEC)) | ||||
|   | ||||
							
								
								
									
										73
									
								
								src/config_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/config_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| from configobj import ConfigObj, ParseError | ||||
| from validate import Validator, ValidateError | ||||
| import os | ||||
| import string | ||||
| class ConfigLoadError(Exception): pass | ||||
|  | ||||
| def load_config(config_path, configspec_path=None, *args, **kwargs): | ||||
|  if os.path.exists(config_path): | ||||
|   clean_config(config_path) | ||||
|  spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True) | ||||
|  try: | ||||
|   config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs) | ||||
|  except ParseError: | ||||
|   raise ConfigLoadError("Unable to load %r" % config_path) | ||||
|  validator = Validator() | ||||
|  validated = config.validate(validator, copy=True) | ||||
|  if validated == True: | ||||
|   config.write() | ||||
|   return config | ||||
|  | ||||
| def is_blank(arg): | ||||
|  "Check if a line is blank." | ||||
|  for c in arg: | ||||
|   if c not in string.whitespace: | ||||
|    return False | ||||
|  return True | ||||
| def get_keys(path): | ||||
|  "Gets the keys of a configobj config file." | ||||
|  res=[] | ||||
|  fin=open(path) | ||||
|  for line in fin: | ||||
|   if not is_blank(line): | ||||
|    res.append(line[0:line.find('=')].strip()) | ||||
|  fin.close() | ||||
|  return res | ||||
|  | ||||
| def hist(keys): | ||||
|  "Generates a histogram of an iterable." | ||||
|  res={} | ||||
|  for k in keys: | ||||
|   res[k]=res.setdefault(k,0)+1 | ||||
|  return res | ||||
|  | ||||
| def find_problems(hist): | ||||
|  "Takes a histogram and returns a list of items occurring more than once." | ||||
|  res=[] | ||||
|  for k,v in hist.items(): | ||||
|   if v>1: | ||||
|    res.append(k) | ||||
|  return res | ||||
|  | ||||
| def clean_config(path): | ||||
|  "Cleans a config file. If duplicate values are found, delete all of them and just use the default." | ||||
|  orig=[] | ||||
|  cleaned=[] | ||||
|  fin=open(path) | ||||
|  for line in fin: | ||||
|   orig.append(line) | ||||
|  fin.close() | ||||
|  for p in find_problems(hist(get_keys(path))): | ||||
|   for o in orig: | ||||
|    o.strip() | ||||
|    if p not in o: | ||||
|     cleaned.append(o) | ||||
|  if len(cleaned) != 0: | ||||
|   cam=open(path,'w') | ||||
|   for c in cleaned: | ||||
|    cam.write(c) | ||||
|   cam.close() | ||||
|   return True | ||||
|  else: | ||||
|   return False | ||||
							
								
								
									
										0
									
								
								src/controller/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/controller/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										65
									
								
								src/controller/buffers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/controller/buffers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| from sessionmanager import session | ||||
| import widgetUtils | ||||
| import messages | ||||
| from wxUI.tabs import home | ||||
| from pubsub import pub | ||||
|  | ||||
| class baseBuffer(object): | ||||
| 	def __init__(self, parent=None, name="", session=None, composefunc=None, *args, **kwargs): | ||||
| 		super(baseBuffer, self).__init__() | ||||
| 		self.args = args | ||||
| 		self.kwargs = kwargs | ||||
| 		self.create_tab(parent) | ||||
| 		self.tab.name = name | ||||
| 		self.session = session | ||||
| 		self.compose_function = composefunc | ||||
| 		self.update_function = "get_page" | ||||
| 		self.name = name | ||||
| 		self.connect_events() | ||||
|  | ||||
| 	def create_tab(self, parent): | ||||
| 		self.tab = home.homeTab(parent) | ||||
|  | ||||
|  | ||||
| 	def insert(self, item, reversed=False): | ||||
| 		item_ = getattr(session, self.compose_function)(item, self.session) | ||||
| 		self.tab.list.insert_item(reversed, *item_) | ||||
|  | ||||
| 	def get_items(self, no_next=True): | ||||
| 		num = getattr(self.session, "get_newsfeed")(no_next=no_next, name=self.name, *self.args, **self.kwargs) | ||||
| 		print num | ||||
| 		if no_next == True: | ||||
| 			if self.tab.list.get_count() > 0 and num > 0: | ||||
| 				print "inserting a value" | ||||
| 				[self.insert(i, True) for i in self.session.db[self.name]["items"][-num:]] | ||||
| 			else: | ||||
| 				[self.insert(i) for i in self.session.db[self.name]["items"][:num]] | ||||
|  | ||||
| 	def post(self, *args, **kwargs): | ||||
| 		p = messages.post(title=_(u"Write your post"), caption="", text="") | ||||
| 		if p.message.get_response() == widgetUtils.OK: | ||||
| 			msg = p.message.get_text().encode("utf-8") | ||||
| 			privacy_opts = p.get_privacy_options() | ||||
| 			self.session.post_wall_status(message=msg, friends_only=privacy_opts) | ||||
| 			pub.sendMessage("posted", buffer=self.name) | ||||
|  | ||||
|  | ||||
| 	def connect_events(self): | ||||
| 		widgetUtils.connect_event(self.tab.post, widgetUtils.BUTTON_PRESSED, self.post) | ||||
|  | ||||
| class feedBuffer(baseBuffer): | ||||
|  | ||||
| 	def get_items(self, no_next=True): | ||||
| 		num = getattr(self.session, "get_page")(no_next=no_next, name=self.name, *self.args, **self.kwargs) | ||||
| 		print num | ||||
| 		if no_next == True: | ||||
| 			if self.tab.list.get_count() > 0 and num > 0: | ||||
| 				print "inserting a value" | ||||
| 				[self.insert(i, True) for i in self.session.db[self.name]["items"][-num:]] | ||||
| 			else: | ||||
| 				[self.insert(i) for i in self.session.db[self.name]["items"][:num]] | ||||
|  | ||||
| class audioBuffer(feedBuffer): | ||||
| 	def create_tab(self, parent): | ||||
| 		self.tab = home.audioTab(parent) | ||||
							
								
								
									
										63
									
								
								src/controller/mainController.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/controller/mainController.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import widgetUtils | ||||
| import messages | ||||
| from wxUI import (mainWindow) | ||||
| import buffers | ||||
| from pubsub import pub | ||||
| from mysc.repeating_timer import RepeatingTimer | ||||
| from sessionmanager import session | ||||
|  | ||||
| class Controller(object): | ||||
|  | ||||
| 	def search(self, tab_name): | ||||
| 		for i in xrange(0, len(self.buffers)): | ||||
| 			if self.buffers[i].name == tab_name: | ||||
| 				return self.buffers[i] | ||||
| 		return False | ||||
|  | ||||
| 	def __init__(self): | ||||
| 		super(Controller, self).__init__() | ||||
| 		self.buffers = [] | ||||
| 		self.window = mainWindow.mainWindow() | ||||
| 		self.window.change_status(_(u"Ready")) | ||||
| 		self.session = session.sessions[session.sessions.keys()[0]] | ||||
| #		self.session.authorise() | ||||
| 		self.create_controls() | ||||
| 		self.window.Show() | ||||
|  | ||||
| 	def create_controls(self): | ||||
| 		home = buffers.baseBuffer(parent=self.window.tb, name="home_timeline", session=self.session, composefunc="compose_new", endpoint="newsfeed", identifier="id") | ||||
| 		self.buffers.append(home) | ||||
| 		self.window.add_buffer(home.tab, _(u"Home")) | ||||
| 		self.repeatedUpdate = RepeatingTimer(180, self.update_all_buffers) | ||||
| 		self.repeatedUpdate.start() | ||||
|  | ||||
| 		feed = buffers.feedBuffer(parent=self.window.tb, name="me_feed", composefunc="compose_status", session=self.session, endpoint="get", parent_endpoint="wall", identifier="id") | ||||
| 		self.buffers.append(feed) | ||||
| 		self.window.add_buffer(feed.tab, _(u"My wall")) | ||||
| 		audio = buffers.audioBuffer(parent=self.window.tb, name="me_audio", composefunc="compose_audio", session=self.session, endpoint="get", parent_endpoint="audio", full_list=True, identifier="aid") | ||||
| 		self.buffers.append(audio) | ||||
| 		self.window.add_buffer(audio.tab, _(u"My audios")) | ||||
| 		pub.subscribe(self.in_post, "posted") | ||||
|  | ||||
| 	def login(self): | ||||
| 		self.window.change_status(_(u"Logging in VK")) | ||||
| 		self.session.authorise() | ||||
| 		self.window.change_status(_(u"Ready")) | ||||
| 		for i in self.buffers: | ||||
| 			if hasattr(i, "get_items"): | ||||
| 				self.window.change_status(_(u"Loading items for {0}").format(i.name,)) | ||||
| 				i.get_items() | ||||
| 		self.window.change_status(_(u"Ready")) | ||||
|  | ||||
| 	def in_post(self, buffer): | ||||
| 		buffer = self.search(buffer) | ||||
| 		buffer.get_items() | ||||
| 		buffer = self.search("home_timeline") | ||||
| 		buffer.get_items() | ||||
|  | ||||
| 	def update_all_buffers(self): | ||||
| 		for i in self.buffers: | ||||
| 			if hasattr(i, "get_items"): | ||||
| 				i.get_items() | ||||
| 				print "executed for %s" % (i.name) | ||||
							
								
								
									
										118
									
								
								src/controller/messages.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/controller/messages.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import widgetUtils | ||||
| import output | ||||
| from pubsub import pub | ||||
| from wxUI.dialogs import message | ||||
| from extra import SpellChecker, translator | ||||
|  | ||||
| class post(object): | ||||
| 	def __init__(self, title, caption, text, post_type="post"): | ||||
| 		super(post, self).__init__() | ||||
| 		self.title = title | ||||
| 		self.message = getattr(message, post_type)(title, caption, text) | ||||
| 		self.message.set_title(title) | ||||
| 		widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) | ||||
| 		widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate) | ||||
| #  self.text_processor() | ||||
| 		self.image = None | ||||
| #		widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image) | ||||
|  | ||||
| 	def get_privacy_options(self): | ||||
| 		p = self.message.get("privacy") | ||||
| 		if p == _(u"Friends of friends"): | ||||
| 			privacy = 0 | ||||
| 		elif p == _(u"All users"): | ||||
| 			privacy = 1 | ||||
| 		return privacy | ||||
|  | ||||
| 	def translate(self, *args, **kwargs): | ||||
| 		dlg = translator.gui.translateDialog() | ||||
| 		if dlg.get_response() == widgetUtils.OK: | ||||
| 			text_to_translate = self.message.get_text().encode("utf-8") | ||||
| 			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_to_translate, source, dest) | ||||
| 			self.message.set_text(msg) | ||||
| 			self.message.text_focus() | ||||
| 			output.speak(_(u"Translated")) | ||||
| 		else: | ||||
| 			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.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.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.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.message.text_focus() | ||||
|  | ||||
| # def text_processor(self, *args, **kwargs): | ||||
| #  self.message.set_title(_(u"%s - %s of 140 characters") % (self.title, len(self.message.get_text()))) | ||||
| #  if len(self.message.get_text()) > 1: | ||||
| #   self.message.enable_button("shortenButton") | ||||
| #   self.message.enable_button("unshortenButton") | ||||
| #  else: | ||||
| #   self.message.disable_button("shortenButton") | ||||
| #   self.message.disable_button("unshortenButton") | ||||
| #  if len(self.message.get_text()) > 140: | ||||
| #   self.session.sound.play("max_length.ogg") | ||||
|  | ||||
| 	def spellcheck(self, event=None): | ||||
| 		text = self.message.get_text() | ||||
| 		checker = SpellChecker.spellchecker.spellChecker(text, "") | ||||
| 		if hasattr(checker, "fixed_text"): | ||||
| 			self.message.set_text(checker.fixed_text) | ||||
|  | ||||
| # def attach(self, *args, **kwargs): | ||||
| #  def completed_callback(): | ||||
| #   url = dlg.uploaderFunction.get_url() | ||||
| #   pub.unsubscribe(dlg.uploaderDialog.update, "uploading") | ||||
| #   dlg.uploaderDialog.destroy() | ||||
| #   if url != 0: | ||||
| #    self.message.set_text(self.message.get_text()+url+" #audio") | ||||
| #   else: | ||||
| #    output.speak(_(u"Unable to upload the audio")) | ||||
| #   dlg.cleanup() | ||||
| #  dlg = audioUploader.audioUploader(self.session.settings, completed_callback) | ||||
|  | ||||
| 	def upload_image(self, *args, **kwargs): | ||||
| 		if self.message.get("upload_image") == _(u"Discard image"): | ||||
| 			del self.image | ||||
| 			self.image = None | ||||
| 			output.speak(_(u"Discarded")) | ||||
| 			self.message.set("upload_image", _(u"Upload a picture")) | ||||
| 		else: | ||||
| 			self.image = self.message.get_image() | ||||
| 			if self.image != None: | ||||
| 				self.message.set("upload_image", _(u"Discard image")) | ||||
|  | ||||
| class comment(post): | ||||
| 	def __init__(self, title, caption, text): | ||||
| 		super(comment, self).__init__(title, caption, text, "comment") | ||||
| 		self.message.set_title(_(u"New comment")) | ||||
							
								
								
									
										189
									
								
								src/controller/posts.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/controller/posts.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import arrow | ||||
| import messages | ||||
| import languageHandler | ||||
| import widgetUtils | ||||
| import output | ||||
| import wx | ||||
| import webbrowser | ||||
| from pubsub import pub | ||||
| from wxUI.dialogs import postDialogs, urlList | ||||
| from extra import SpellChecker, translator | ||||
| from mysc.thread_utils import call_threaded | ||||
| from wxUI import menus | ||||
| from utils import find_urls | ||||
|  | ||||
| class postController(object): | ||||
| 	def __init__(self, session, postObject): | ||||
| 		super(postController, self).__init__() | ||||
| 		self.session = session | ||||
| 		self.post = postObject | ||||
| 		self.dialog = postDialogs.post() | ||||
| 		self.dialog.comments.list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.show_comment) | ||||
| 		from_ = self.session.get_user_name(self.post["from_id"]) | ||||
| #		if self.post.has_key("owner_id"): | ||||
| #			to_ = [i["name"] for i in self.post["to"]["data"]] | ||||
| #			title = _(u"Post from {0} in {1}").format(from_, "".join(to_)) | ||||
| #		else: | ||||
| 		title = _(u"Post from {0}").format(from_,) | ||||
| 		self.dialog.set_title(title) | ||||
| 		message = story = u"" | ||||
| 		if self.post.has_key("message"): | ||||
| 			message = self.post["message"] | ||||
| 		if self.post.has_key("story"): | ||||
| 			story = self.post["story"] | ||||
| 		if self.post.has_key("name") and self.post.has_key("link"): | ||||
| 			message += u". {0}, {1}".format(self.post["name"], self.post["link"]) | ||||
| 		if story != "": | ||||
| 			final_msg = u"{0} \n\n{1}".format(story, message) | ||||
| 		else: | ||||
| 			final_msg = message | ||||
| 		self.dialog.set_post(final_msg) | ||||
| #  widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) | ||||
| #  widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate) | ||||
| #  self.text_processor() | ||||
| 		widgetUtils.connect_event(self.dialog.like, widgetUtils.BUTTON_PRESSED, self.post_like) | ||||
| 		widgetUtils.connect_event(self.dialog.comment, widgetUtils.BUTTON_PRESSED, self.add_comment) | ||||
| 		widgetUtils.connect_event(self.dialog.tools, widgetUtils.BUTTON_PRESSED, self.show_tools_menu) | ||||
| 		self.dialog.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu, self.dialog.comments.list) | ||||
| 		self.dialog.Bind(wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key, self.dialog.comments.list) | ||||
| 		call_threaded(self.load_all_components) | ||||
|  | ||||
| 	def load_all_components(self): | ||||
| 		self.get_likes() | ||||
| 		self.get_shares() | ||||
| 		self.get_comments() | ||||
|  | ||||
| 	def post_like(self, *args, **kwargs): | ||||
| 		lk = self.session.like(self.post["id"]) | ||||
| 		self.get_likes() | ||||
|  | ||||
| 	def get_likes(self): | ||||
| 		self.likes = self.session.fb.client.get_connections(id=self.post["id"], connection_name="likes", summary=True) | ||||
| 		self.dialog.set_likes(self.likes["summary"]["total_count"]) | ||||
|  | ||||
| 	def get_shares(self): | ||||
| 		self.shares = self.session.fb.client.get_connections(id=self.post["id"], connection_name="sharedposts") | ||||
| 		self.dialog.set_shares(str(len(self.shares["data"]))) | ||||
|  | ||||
| 	def get_comments(self): | ||||
| 		self.comments = self.session.fb.client.get_connections(id=self.post["id"], connection_name="comments", filter="stream") | ||||
| 		comments = [] | ||||
| 		for i in self.comments["data"]: | ||||
| 			from_ = i["from"]["name"] | ||||
| 			if len(i["message"]) > 100: | ||||
| 				comment = i["message"][:100] | ||||
| 			else: | ||||
| 				comment = i["message"] | ||||
| 			original_date = arrow.get(i["created_time"], "YYYY-MM-DTHH:m:sZ", locale="en") | ||||
| 			created_at = original_date.humanize(locale=languageHandler.getLanguage()) | ||||
| 			likes = str(i["like_count"]) | ||||
| 			comments.append([from_, comment, created_at, likes,]) | ||||
| 		self.dialog.insert_comments(comments) | ||||
|  | ||||
| 	def add_comment(self, *args, **kwargs): | ||||
| 		comment = messages.comment(title=_(u"Add a comment"), caption="", text="") | ||||
| 		if comment.message.get_response() == widgetUtils.OK: | ||||
| 			msg = comment.message.get_text().encode("utf-8") | ||||
| 			try: | ||||
| 				self.session.fb.client.put_comment(self.post["id"], msg) | ||||
| 				output.speak(_(u"You've posted a comment")) | ||||
| 				if len(self.comments["data"]) < 25: | ||||
| 					self.clear_comments_list() | ||||
| 					self.get_comments() | ||||
| 			except Exception as msg: | ||||
| 				print msg | ||||
|  | ||||
| 	def clear_comments_list(self): | ||||
| 		self.dialog.comments.clear() | ||||
|  | ||||
| 	def show_comment(self, *args, **kwargs): | ||||
| 		c = comment(self.session, self.comments["data"][self.dialog.comments.get_selected()]) | ||||
| 		c.dialog.get_response() | ||||
|  | ||||
| 	def show_menu(self, *args, **kwargs): | ||||
| 		if self.dialog.comments.get_count() == 0: return | ||||
| 		menu = menus.commentMenu() | ||||
| 		widgetUtils.connect_event(self.dialog, widgetUtils.MENU, self.show_comment, menuitem=menu.open) | ||||
| 		widgetUtils.connect_event(self.dialog, widgetUtils.MENU, self.comment_like, menuitem=menu.like) | ||||
| 		widgetUtils.connect_event(self.dialog, widgetUtils.MENU, self.comment_unlike, menuitem=menu.unlike) | ||||
| 		self.dialog.PopupMenu(menu, self.dialog.comments.list.GetPosition()) | ||||
|  | ||||
| 	def show_menu_by_key(self, ev): | ||||
| 		if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU: | ||||
| 			self.show_menu() | ||||
|  | ||||
| 	def show_tools_menu(self, *args, **kwargs): | ||||
| 		menu = menus.toolsMenu() | ||||
| 		widgetUtils.connect_event(self.dialog, widgetUtils.MENU, self.open_url, menuitem=menu.url) | ||||
| 		widgetUtils.connect_event(self.dialog, widgetUtils.MENU, self.translate, menuitem=menu.translate) | ||||
| 		widgetUtils.connect_event(self.dialog, widgetUtils.MENU, self.spellcheck, menuitem=menu.CheckSpelling) | ||||
| 		self.dialog.PopupMenu(menu, self.dialog.tools.GetPosition()) | ||||
|  | ||||
| 	def comment_like(self, *args, **kwargs): | ||||
| 		comment_id = self.comments["data"][self.dialog.comments.get_selected()]["id"] | ||||
| 		self.session.like(comment_id) | ||||
| 		output.speak(_(u"You do like this comment")) | ||||
|  | ||||
| 	def comment_unlike(self, *args, **kwargs): | ||||
| 		comment_id = self.comments["data"][self.dialog.comments.get_selected()]["id"] | ||||
| 		self.session.unlike(comment_id) | ||||
| 		output.speak(_(u"You don't like this comment")) | ||||
|  | ||||
| 	def translate(self, *args, **kwargs): | ||||
| 		dlg = translator.gui.translateDialog() | ||||
| 		if dlg.get_response() == widgetUtils.OK: | ||||
| 			text_to_translate = self.dialog.post_view.GetValue().encode("utf-8") | ||||
| 			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_to_translate, source, dest) | ||||
| 			self.dialog.post_view.ChangeValue(msg) | ||||
| 			output.speak(_(u"Translated")) | ||||
| 		else: | ||||
| 			return | ||||
|  | ||||
| 	def spellcheck(self, *args, **kwargs): | ||||
| 		text = self.dialog.post_view.GetValue() | ||||
| 		checker = SpellChecker.spellchecker.spellChecker(text, "") | ||||
| 		if hasattr(checker, "fixed_text"): | ||||
| 			self.dialog.post_view.ChangeValue(checker.fixed_text) | ||||
|  | ||||
| 	def open_url(self, *args, **kwargs): | ||||
| 		text = self.dialog.post_view.GetValue() | ||||
| 		urls = find_urls(text) | ||||
| 		url = None | ||||
| 		if len(urls) == 0: return | ||||
| 		if len(urls) == 1: | ||||
| 			url = urls[0] | ||||
| 		elif len(urls) > 1: | ||||
| 			url_list = urlList.urlList() | ||||
| 			url_list.populate_list(urls) | ||||
| 			if url_list.get_response() == widgetUtils.OK: | ||||
| 				url = urls[url_list.get_item()] | ||||
| 		if url != None: | ||||
| 			output.speak(_(u"Opening URL..."), True) | ||||
| 			webbrowser.open_new_tab(url) | ||||
|  | ||||
| class comment(object): | ||||
| 	def __init__(self, session, comment_object): | ||||
| 		super(comment, self).__init__() | ||||
| 		self.session = session | ||||
| 		self.comment = comment_object | ||||
| 		self.dialog = postDialogs.comment() | ||||
| 		from_ = self.comment["from"]["name"] | ||||
| 		message = self.comment["message"] | ||||
| 		original_date = arrow.get(self.comment["created_time"], "YYYY-MM-DTHH:m:sZ", locale="en") | ||||
| 		created_at = original_date.humanize(locale=languageHandler.getLanguage()) | ||||
| 		self.dialog.set_post(message) | ||||
| 		self.dialog.set_title(_(u"Comment from {0}").format(from_,)) | ||||
| 		widgetUtils.connect_event(self.dialog.like, widgetUtils.BUTTON_PRESSED, self.post_like) | ||||
| 		call_threaded(self.get_likes) | ||||
|  | ||||
| 	def get_likes(self): | ||||
| 		self.likes = self.session.fb.client.get_connections(id=self.comment["id"], connection_name="likes", summary=True) | ||||
| 		self.dialog.set_likes(self.likes["summary"]["total_count"]) | ||||
|  | ||||
| 	def post_like(self, *args, **kwargs): | ||||
| 		lk = self.session.like(self.comment["id"]) | ||||
| 		self.get_likes() | ||||
|  | ||||
							
								
								
									
										0
									
								
								src/extra/AudioUploader/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/extra/AudioUploader/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										186
									
								
								src/extra/AudioUploader/audioUploader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/extra/AudioUploader/audioUploader.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| ############################################################ | ||||
| #    Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net> | ||||
| #        | ||||
| #    This program is free software: you can redistribute it and/or modify | ||||
| #    it under the terms of the GNU General Public License as published by | ||||
| #    the Free Software Foundation, either version 2 of the License, or | ||||
| #    (at your option) any later version. | ||||
| # | ||||
| #    This program is distributed in the hope that it will be useful, | ||||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #    GNU General Public License for more details. | ||||
| # | ||||
| #    You should have received a copy of the GNU General Public License | ||||
| #    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ############################################################ | ||||
| import widgetUtils | ||||
| import wx_ui | ||||
| import wx_transfer_dialogs | ||||
| import transfer | ||||
| import output | ||||
| import tempfile | ||||
| import sound | ||||
| import os | ||||
| import config | ||||
| from pubsub import pub | ||||
| from mysc.thread_utils import call_threaded | ||||
| import sound_lib | ||||
| import logging | ||||
|  | ||||
| log = logging.getLogger("extra.AudioUploader.audioUploader") | ||||
|  | ||||
| class audioUploader(object): | ||||
|  def __init__(self, configFile, completed_callback): | ||||
|   self.config = configFile | ||||
|   super(audioUploader, self).__init__() | ||||
|   self.dialog = wx_ui.audioDialog(services=self.get_available_services()) | ||||
|   self.file = None | ||||
|   self.recorded = False | ||||
|   self.recording = None | ||||
|   self.playing = None | ||||
|   widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play) | ||||
|   widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause) | ||||
|   widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record) | ||||
|   widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists) | ||||
|   widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard) | ||||
|   if self.dialog.get_response() == widgetUtils.OK: | ||||
|    self.postprocess() | ||||
|    log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services"))) | ||||
|    self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file) | ||||
|    output.speak(_(u"Attaching...")) | ||||
|    if self.dialog.get("services") == "SNDUp": | ||||
|     base_url = "http://sndup.net/post.php" | ||||
|     if len(self.config["sound"]["sndup_api_key"]) > 0: | ||||
|      url = base_url + '?apikey=' + self.config['sound']['sndup_api_key'] | ||||
|     else: | ||||
|      url = base_url | ||||
|     self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback) | ||||
|    elif self.dialog.get("services") == "TwUp": | ||||
|     url = "http://api.twup.me/post.json" | ||||
|     self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback) | ||||
|    pub.subscribe(self.uploaderDialog.update, "uploading") | ||||
|    self.uploaderDialog.get_response() | ||||
|    self.uploaderFunction.perform_threaded() | ||||
|  | ||||
|  def get_available_services(self): | ||||
|   services = [] | ||||
|   services.append("TwUp") | ||||
|   services.append("SNDUp") | ||||
|   return services | ||||
|  | ||||
|  def on_pause(self, *args, **kwargs): | ||||
|   if self.dialog.get("pause") == _(u"Pause"): | ||||
|    self.recording.pause() | ||||
|    self.dialog.set("pause", _(u"&Resume")) | ||||
|   elif self.dialog.get("pause") == _(u"Resume"): | ||||
|    self.recording.play() | ||||
|    self.dialog.set("pause", _(U"&Pause")) | ||||
|  | ||||
|  def on_record(self, *args, **kwargs): | ||||
|   if self.recording != None: | ||||
|    self.stop_recording() | ||||
|    self.dialog.disable_control("pause") | ||||
|   else: | ||||
|    self.start_recording() | ||||
|    self.dialog.enable_control("pause") | ||||
|  | ||||
|  def start_recording(self): | ||||
|   self.dialog.disable_control("attach_exists") | ||||
|   self.file = tempfile.mktemp(suffix='.wav') | ||||
|   self.recording = sound.recording(self.file) | ||||
|   self.recording.play() | ||||
|   self.dialog.set("record", _(u"&Stop")) | ||||
|   output.speak(_(u"Recording")) | ||||
|  | ||||
|  def stop_recording(self): | ||||
|   self.recording.stop() | ||||
|   self.recording.free() | ||||
|   output.speak(_(u"Stopped")) | ||||
|   self.recorded = True | ||||
|   self.dialog.set("record", _(u"&Record")) | ||||
|   self.file_attached() | ||||
|  | ||||
|  def file_attached(self): | ||||
|   self.dialog.set("pause", _(u"&Pause")) | ||||
|   self.dialog.disable_control("record") | ||||
|   self.dialog.enable_control("play") | ||||
|   self.dialog.enable_control("discard") | ||||
|   self.dialog.disable_control("attach_exists") | ||||
|   self.dialog.enable_control("attach") | ||||
|   self.dialog.play.SetFocus() | ||||
|  | ||||
|  def on_discard(self, *args, **kwargs): | ||||
|   if self.playing: | ||||
|    self._stop() | ||||
|   if self.recording != None: | ||||
|    self.dialog.disable_control("attach") | ||||
|    self.dialog.disable_control("play") | ||||
|   self.file = None | ||||
|   self.dialog.enable_control("record") | ||||
|   self.dialog.enable_control("attach_exists") | ||||
|   self.dialog.record.SetFocus() | ||||
|   self.dialog.disable_control("discard") | ||||
|   self.recording = None | ||||
|   output.speak(_(u"Discarded")) | ||||
|  | ||||
|  def on_play(self, *args, **kwargs): | ||||
|   if not self.playing: | ||||
|    call_threaded(self._play) | ||||
|   else: | ||||
|    self._stop() | ||||
|  | ||||
|  def _play(self): | ||||
|   output.speak(_(u"Playing...")) | ||||
| #  try: | ||||
|   self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE) | ||||
|   self.playing.play() | ||||
|   self.dialog.set("play", _(u"&Stop")) | ||||
|   try: | ||||
|    while self.playing.is_playing: | ||||
|     pass | ||||
|    self.dialog.set("play", _(u"&Play")) | ||||
|    self.playing.free() | ||||
|    self.playing = None | ||||
|   except: | ||||
|    pass | ||||
|  | ||||
|  def _stop(self): | ||||
|   output.speak(_(u"Stopped")) | ||||
|   self.playing.stop() | ||||
|   self.playing.free() | ||||
|   self.dialog.set("play", _(u"&Play")) | ||||
|   self.playing = None | ||||
|  | ||||
|  def postprocess(self): | ||||
|   if self.file.lower().endswith('.wav'): | ||||
|    output.speak(_(u"Recoding audio...")) | ||||
|    sound.recode_audio(self.file) | ||||
|    self.wav_file = self.file | ||||
|    self.file = '%s.ogg' % self.file[:-4] | ||||
|  | ||||
|  def cleanup(self): | ||||
|   if self.playing and self.playing.is_playing: | ||||
|    self.playing.stop() | ||||
|   if self.recording != None: | ||||
|    if self.recording.is_playing: | ||||
|     self.recording.stop() | ||||
|    try: | ||||
|     self.recording.free() | ||||
|    except: | ||||
|     pass | ||||
|    os.remove(self.file) | ||||
|    if hasattr(self, 'wav_file'): | ||||
|     os.remove(self.wav_file) | ||||
|     del(self.wav_file) | ||||
|   if hasattr(self, 'wav_file') and os.path.exists(self.file): | ||||
|    os.remove(self.file) | ||||
|  | ||||
|  | ||||
|  def on_attach_exists(self, *args, **kwargs): | ||||
|   self.file = self.dialog.get_file() | ||||
|   if self.file != False: | ||||
|    self.file_attached() | ||||
|  | ||||
							
								
								
									
										106
									
								
								src/extra/AudioUploader/transfer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/extra/AudioUploader/transfer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import pycurl | ||||
| import sys | ||||
| import threading | ||||
| import time | ||||
| import json | ||||
| import logging | ||||
| from utils import * | ||||
| from pubsub import pub | ||||
|  | ||||
| log = logging.getLogger("extra.AudioUploader.transfer") | ||||
| class Transfer(object): | ||||
|  | ||||
|  def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs): | ||||
|   self.url = url | ||||
|   self.filename = filename | ||||
|   log.debug("Uploading audio to %s, filename %s" % (url, filename)) | ||||
|   self.curl = pycurl.Curl() | ||||
|   self.start_time = None | ||||
|   self.completed_callback = completed_callback | ||||
|   self.background_thread = None | ||||
|   self.transfer_rate = 0 | ||||
|   self.curl.setopt(self.curl.PROGRESSFUNCTION, self.progress_callback) | ||||
|   self.curl.setopt(self.curl.URL, url) | ||||
|   self.curl.setopt(self.curl.NOPROGRESS, 0) | ||||
|   self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0) | ||||
|   self.curl.setopt(self.curl.FOLLOWLOCATION, int(follow_location)) | ||||
|   self.curl.setopt(self.curl.VERBOSE, int(verbose)) | ||||
|   super(Transfer, self).__init__(*args, **kwargs) | ||||
|  | ||||
|  def elapsed_time(self): | ||||
|   if not self.start_time: | ||||
|    return 0 | ||||
|   return time.time() - self.start_time | ||||
|  | ||||
|  def progress_callback(self, down_total, down_current, up_total, up_current): | ||||
|   progress = {} | ||||
|   progress["total"] = up_total | ||||
|   progress["current"] = up_current | ||||
| #  else: | ||||
| #   print "Killed function" | ||||
| #   return | ||||
|   if progress["current"] == 0: | ||||
|    progress["percent"] = 0 | ||||
|    self.transfer_rate = 0 | ||||
|   else: | ||||
|    progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100) | ||||
|    self.transfer_rate = progress["current"] / self.elapsed_time() | ||||
|   progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate) | ||||
|   if self.transfer_rate: | ||||
|    progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate | ||||
|   else: | ||||
|    progress["eta"] = 0 | ||||
|   pub.sendMessage("uploading", data=progress) | ||||
|  | ||||
|  def perform_transfer(self): | ||||
|   log.debug("starting upload...") | ||||
|   self.start_time = time.time() | ||||
|   self.curl.perform() | ||||
|   self.curl.close() | ||||
|   log.debug("Upload finished.") | ||||
|   self.complete_transfer() | ||||
|  | ||||
|  def perform_threaded(self): | ||||
|   self.background_thread = threading.Thread(target=self.perform_transfer) | ||||
|   self.background_thread.daemon = True | ||||
|   self.background_thread.start() | ||||
|  | ||||
|  def complete_transfer(self): | ||||
|   if callable(self.completed_callback): | ||||
|    self.curl.close() | ||||
|    self.completed_callback() | ||||
|  | ||||
| class Upload(Transfer): | ||||
|  | ||||
|  def __init__(self, field=None, filename=None, *args, **kwargs): | ||||
|   super(Upload, self).__init__(filename=filename, *args, **kwargs) | ||||
|   self.response = dict() | ||||
|   self.curl.setopt(self.curl.POST, 1) | ||||
|   if isinstance(filename, unicode): | ||||
|    local_filename = filename.encode(sys.getfilesystemencoding()) | ||||
|   else: | ||||
|    local_filename = filename | ||||
|   self.curl.setopt(self.curl.HTTPPOST, [(field, (self.curl.FORM_FILE, local_filename, self.curl.FORM_FILENAME, filename.encode("utf-8")))]) | ||||
|   self.curl.setopt(self.curl.HEADERFUNCTION, self.header_callback) | ||||
|   self.curl.setopt(self.curl.WRITEFUNCTION, self.body_callback) | ||||
|  | ||||
|  def header_callback(self, content): | ||||
|   self.response['header'] = content | ||||
|  | ||||
|  def body_callback(self, content): | ||||
|   self.response['body'] = content | ||||
|  | ||||
|  def get_url(self): | ||||
|   return json.loads(self.response['body'])['url'] | ||||
|  | ||||
| class Download(Transfer): | ||||
|  | ||||
|  def __init__(self, follow_location=True, *args, **kwargs): | ||||
|   super(Download, self).__init__(*args, **kwargs) | ||||
|   self.download_file = open(self.filename, 'wb') | ||||
|   self.curl.setopt(self.curl.WRITEFUNCTION, self.download_file.write) | ||||
|  | ||||
|  def complete_transfer(self): | ||||
|   self.download_file.close() | ||||
|   super(DownloadDialog, self).complete_transfer() | ||||
							
								
								
									
										42
									
								
								src/extra/AudioUploader/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/extra/AudioUploader/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| def convert_bytes(n): | ||||
|  K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50 | ||||
|  if   n >= P: | ||||
|   return '%.2fPb' % (float(n) / T) | ||||
|  elif   n >= T: | ||||
|   return '%.2fTb' % (float(n) / T) | ||||
|  elif n >= G: | ||||
|   return '%.2fGb' % (float(n) / G) | ||||
|  elif n >= M: | ||||
|   return '%.2fMb' % (float(n) / M) | ||||
|  elif n >= K: | ||||
|   return '%.2fKb' % (float(n) / K) | ||||
|  else: | ||||
|   return '%d' % n | ||||
|  | ||||
| def seconds_to_string(seconds, precision=0): | ||||
|  day = seconds // 86400 | ||||
|  hour = seconds // 3600 | ||||
|  min = (seconds // 60) % 60 | ||||
|  sec = seconds - (hour * 3600) - (min * 60) | ||||
|  sec_spec = "." + str(precision) + "f" | ||||
|  sec_string = sec.__format__(sec_spec) | ||||
|  string = "" | ||||
|  if day == 1: | ||||
|   string += _(u"%d day, ") % day | ||||
|  elif day >= 2: | ||||
|   string += _(u"%d days, ") % day | ||||
|  if (hour == 1): | ||||
|   string += _(u"%d hour, ") % hour | ||||
|  elif (hour >= 2): | ||||
|   string += _("%d hours, ") % hour | ||||
|  if (min == 1): | ||||
|   string += _(u"%d minute, ") % min | ||||
|  elif (min >= 2): | ||||
|   string += _(u"%d minutes, ") % min | ||||
|  if sec >= 0 and sec <= 2: | ||||
|   string += _(u"%s second") % sec_string | ||||
|  else: | ||||
|   string += _(u"%s seconds") % sec_string | ||||
|  return string | ||||
							
								
								
									
										73
									
								
								src/extra/AudioUploader/wx_transfer_dialogs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/extra/AudioUploader/wx_transfer_dialogs.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| from utils import * | ||||
| import widgetUtils | ||||
|  | ||||
| class TransferDialog(widgetUtils.BaseDialog): | ||||
|  | ||||
|  def __init__(self, filename, *args, **kwargs): | ||||
|   super(TransferDialog, self).__init__(parent=None, id=wx.NewId(), *args, **kwargs) | ||||
|   self.pane = wx.Panel(self) | ||||
|   self.progress_bar = wx.Gauge(parent=self.pane) | ||||
|   fileBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   fileLabel = wx.StaticText(self.pane, -1, _(u"File")) | ||||
|   self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100)) | ||||
|   self.file.SetFocus() | ||||
|   fileBox.Add(fileLabel) | ||||
|   fileBox.Add(self.file) | ||||
|   currentAmountBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred")) | ||||
|   self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE) | ||||
|   currentAmountBox.Add(current_amount_label) | ||||
|   currentAmountBox.Add(self.current_amount) | ||||
|   totalSizeBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size")) | ||||
|   self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE) | ||||
|   totalSizeBox.Add(total_size_label) | ||||
|   totalSizeBox.Add(self.total_size) | ||||
|   speedBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate")) | ||||
|   self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s") | ||||
|   speedBox.Add(speedLabel) | ||||
|   speedBox.Add(self.speed) | ||||
|   etaBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   etaLabel = wx.StaticText(self.pane, -1, _(u"Time left")) | ||||
|   self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100)) | ||||
|   etaBox.Add(etaLabel) | ||||
|   etaBox.Add(self.eta) | ||||
|   self.create_buttons() | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   sizer.Add(fileBox) | ||||
|   sizer.Add(currentAmountBox) | ||||
|   sizer.Add(totalSizeBox) | ||||
|   sizer.Add(speedBox) | ||||
|   sizer.Add(etaBox) | ||||
|   sizer.Add(self.progress_bar) | ||||
|   self.pane.SetSizerAndFit(sizer) | ||||
|  | ||||
|  def update(self, data): | ||||
|   wx.CallAfter(self.progress_bar.SetValue, data["percent"]) | ||||
|   wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"])) | ||||
|   wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"])) | ||||
|   wx.CallAfter(self.speed.SetValue, data["speed"]) | ||||
|   if data["eta"]: | ||||
|    wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"])) | ||||
|  | ||||
|  def create_buttons(self): | ||||
|   self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL) | ||||
|  | ||||
|  def get_response(self): | ||||
|   self.Show() | ||||
|  | ||||
|  def destroy(self): | ||||
|   self.Destroy() | ||||
|  | ||||
| class UploadDialog(TransferDialog): | ||||
|  | ||||
|  def __init__(self, filename=None, *args, **kwargs): | ||||
|   super(UploadDialog, self).__init__(filename=filename, *args, **kwargs) | ||||
|  | ||||
| class DownloadDialog(TransferDialog): | ||||
|  | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super(Download, self).__init__(*args, **kwargs) | ||||
							
								
								
									
										78
									
								
								src/extra/AudioUploader/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/extra/AudioUploader/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| ############################################################ | ||||
| #    Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net> | ||||
| #        | ||||
| #    This program is free software: you can redistribute it and/or modify | ||||
| #    it under the terms of the GNU General Public License as published by | ||||
| #    the Free Software Foundation, either version 2 of the License, or | ||||
| #    (at your option) any later version. | ||||
| # | ||||
| #    This program is distributed in the hope that it will be useful, | ||||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #    GNU General Public License for more details. | ||||
| # | ||||
| #    You should have received a copy of the GNU General Public License | ||||
| #    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ############################################################ | ||||
| import wx | ||||
| import widgetUtils | ||||
| import output | ||||
| import logging | ||||
| log = logging.getLogger("extra.AudioUploader.wx_UI") | ||||
|  | ||||
| class audioDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self, services): | ||||
|   log.debug("creating audio  dialog.") | ||||
|   super(audioDialog, self).__init__(None, -1, _(u"Attach audio")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   btnSizer = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   btnSizer2 = wx.BoxSizer(wx.HORIZONTAL) | ||||
|  | ||||
|   self.play = wx.Button(panel, -1, _(u"&Play")) | ||||
|   self.play.Disable() | ||||
|   self.pause = wx.Button(panel, -1, _(u"&Pause")) | ||||
|   self.pause.Disable() | ||||
|   self.record = wx.Button(panel, -1, _(u"&Record")) | ||||
|   self.record.SetFocus() | ||||
|   self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file")) | ||||
|   self.discard = wx.Button(panel, -1, _(u"&Discard")) | ||||
|   self.discard.Disable() | ||||
|   label = wx.StaticText(panel, -1, _(u"Upload to")) | ||||
|   self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY) | ||||
|   servicesBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   servicesBox.Add(label, 0, wx.ALL, 5) | ||||
|   servicesBox.Add(self.services, 0, wx.ALL, 5) | ||||
|   self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach")) | ||||
|   self.attach.Disable() | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel")) | ||||
|   btnSizer.Add(self.play, 0, wx.ALL, 5) | ||||
|   btnSizer.Add(self.pause, 0, wx.ALL, 5) | ||||
|   btnSizer.Add(self.record, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(self.discard, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(self.attach, 0, wx.ALL, 5) | ||||
|   btnSizer2.Add(cancel, 0, wx.ALL, 5) | ||||
|   sizer.Add(servicesBox, 0, wx.ALL, 5) | ||||
|   sizer.Add(btnSizer, 0, wx.ALL, 5) | ||||
|   sizer.Add(btnSizer2, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  def enable_control(self, control): | ||||
|   log.debug("Enabling control %s" % (control,)) | ||||
|   if hasattr(self, control): | ||||
|    getattr(self, control).Enable() | ||||
|  | ||||
|  def disable_control(self, control): | ||||
|   log.debug("Disabling control %s" % (control,)) | ||||
|   if hasattr(self, control): | ||||
|    getattr(self, control).Disable() | ||||
|  | ||||
|  def get_file(self): | ||||
|   openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) | ||||
|   if openFileDialog.ShowModal() == wx.ID_CANCEL: | ||||
|    return False | ||||
|   return openFileDialog.GetPath() | ||||
							
								
								
									
										1
									
								
								src/extra/SoundsTutorial/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/extra/SoundsTutorial/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| from soundsTutorial import soundsTutorial | ||||
							
								
								
									
										25
									
								
								src/extra/SoundsTutorial/gtk_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/extra/SoundsTutorial/gtk_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| from gi.repository import Gtk | ||||
| import widgetUtils | ||||
|  | ||||
| class soundsTutorialDialog(Gtk.Dialog): | ||||
|  def __init__(self, actions): | ||||
|   super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL)) | ||||
|   box = self.get_content_area() | ||||
|   label = Gtk.Label("Press enter for listen the sound") | ||||
|   self.list = widgetUtils.list("Action") | ||||
|   self.populate_actions(actions) | ||||
|   lBox = Gtk.Box(spacing=6) | ||||
|   lBox.add(label) | ||||
|   lBox.add(self.list.list) | ||||
|   box.add(lBox) | ||||
|   self.play = Gtk.Button("Play") | ||||
|   box.add(self.play) | ||||
|   self.show_all() | ||||
|  | ||||
|  def populate_actions(self, actions): | ||||
|   for i in actions: | ||||
|    self.list.insert_item(i) | ||||
|  | ||||
|  def get_selected(self): | ||||
|   return self.list.get_selected() | ||||
							
								
								
									
										11
									
								
								src/extra/SoundsTutorial/reverse_sort.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/extra/SoundsTutorial/reverse_sort.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #Reverse sort, by Bill Dengler <codeofdusk@gmail.com> for use in TWBlue http://twblue.es | ||||
| def invert_tuples(t): | ||||
|     "Invert a list of tuples, so that the 0th element becomes the -1th, and the -1th becomes the 0th." | ||||
|     res=[] | ||||
|     for i in t: | ||||
|         res.append(i[::-1]) | ||||
|     return res | ||||
|  | ||||
| def reverse_sort(t): | ||||
|     "Sorts a list of tuples/lists by their last elements, not their first." | ||||
|     return invert_tuples(sorted(invert_tuples(t))) | ||||
							
								
								
									
										34
									
								
								src/extra/SoundsTutorial/soundsTutorial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/extra/SoundsTutorial/soundsTutorial.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import platform | ||||
| import widgetUtils | ||||
| import os | ||||
| import paths | ||||
| import logging | ||||
| log = logging.getLogger("extra.SoundsTutorial.soundsTutorial") | ||||
| import soundsTutorial_constants | ||||
| if platform.system() == "Windows": | ||||
|  import wx_ui as UI | ||||
| elif platform.system() == "Linux": | ||||
|  import gtk_ui as UI | ||||
|  | ||||
| class soundsTutorial(object): | ||||
|  def __init__(self, sessionObject): | ||||
|   log.debug("Creating sounds tutorial object...") | ||||
|   super(soundsTutorial, self).__init__() | ||||
|   self.session = sessionObject | ||||
|   self.actions = [] | ||||
|   log.debug("Loading actions for sounds tutorial...") | ||||
|   [self.actions.append(i[1]) for i in soundsTutorial_constants.actions] | ||||
|   self.files = [] | ||||
|   log.debug("Searching sound files...") | ||||
|   [self.files.append(i[0]) for i in soundsTutorial_constants.actions] | ||||
|   log.debug("Creating dialog...") | ||||
|   self.dialog = UI.soundsTutorialDialog(self.actions) | ||||
|   widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play) | ||||
|   self.dialog.get_response() | ||||
|  | ||||
|  def on_play(self, *args, **kwargs): | ||||
|   try: | ||||
|    self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg") | ||||
|   except: | ||||
|    log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],)) | ||||
							
								
								
									
										27
									
								
								src/extra/SoundsTutorial/soundsTutorial_constants.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/extra/SoundsTutorial/soundsTutorial_constants.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #-*- coding: utf-8 -*- | ||||
| import reverse_sort | ||||
| import application | ||||
| actions = reverse_sort.reverse_sort([  ("audio", _(u"Audio tweet.")), | ||||
|   ("create_timeline", _(u"User timeline buffer created.")), | ||||
|     ("delete_timeline", _(u"Buffer destroied.")), | ||||
|     ("dm_received", _(u"Direct message received.")), | ||||
|     ("dm_sent", _(u"Direct message sent.")), | ||||
|     ("error", _(u"Error.")), | ||||
|   ("favourite", _(u"Tweet liked.")), | ||||
|   ("favourites_timeline_updated", _(u"Likes buffer updated.")), | ||||
|   ("geo",   _(u"Geotweet.")), | ||||
| ("limit", _(u"Boundary reached.")), | ||||
|     ("list_tweet", _(u"List updated.")), | ||||
|     ("max_length", _(u"Too many characters.")), | ||||
|     ("mention_received", _(u"Mention received.")), | ||||
|   ("new_event", _(u"New event.")), | ||||
|   ("ready", _(u"{0} is ready.").format(application.name,)), | ||||
|     ("reply_send", _(u"Mention sent.")), | ||||
|     ("retweet_send", _(u"Tweet retweeted.")), | ||||
|     ("search_updated", _(u"Search buffer updated.")), | ||||
|     ("tweet_received", _(u"Tweet received.")), | ||||
|     ("tweet_send", _(u"Tweet sent.")), | ||||
|     ("trends_updated", _(u"Trending topics buffer updated.")), | ||||
|     ("tweet_timeline", _(u"New tweet in user timeline buffer.")), | ||||
|     ("update_followers", _(u"New follower.")), | ||||
|     ("volume_changed", _(u"Volume changed."))]) | ||||
							
								
								
									
										29
									
								
								src/extra/SoundsTutorial/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/extra/SoundsTutorial/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
|  | ||||
| class soundsTutorialDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self, actions): | ||||
|   super(soundsTutorialDialog, self).__init__(None, -1) | ||||
|   self.SetTitle(_(u"Sounds tutorial")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event")) | ||||
|   self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE) | ||||
|   self.items.SetSelection(0) | ||||
|   listBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   listBox.Add(label) | ||||
|   listBox.Add(self.items) | ||||
|   self.play = wx.Button(panel, 1, (u"Play")) | ||||
|   self.play.SetDefault() | ||||
|   close = wx.Button(panel, wx.ID_CANCEL) | ||||
|   btnBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   btnBox.Add(self.play) | ||||
|   btnBox.Add(close) | ||||
|   sizer.Add(listBox) | ||||
|   sizer.Add(btnBox) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  def get_selection(self): | ||||
|   return self.items.GetSelection() | ||||
							
								
								
									
										4
									
								
								src/extra/SpellChecker/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/extra/SpellChecker/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| import spellchecker | ||||
| import platform | ||||
| if platform.system() == "Windows": | ||||
|  from wx_ui import * | ||||
							
								
								
									
										70
									
								
								src/extra/SpellChecker/spellchecker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/extra/SpellChecker/spellchecker.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import logging | ||||
| log = logging.getLogger("extra.SpellChecker.spellChecker") | ||||
| import wx_ui | ||||
| import widgetUtils | ||||
| import output | ||||
| import config | ||||
| import languageHandler | ||||
| from enchant.checker import SpellChecker | ||||
| from enchant.errors import DictNotFoundError | ||||
| from enchant import tokenize | ||||
| import twitterFilter | ||||
|  | ||||
| class spellChecker(object): | ||||
|  def __init__(self, text, dictionary): | ||||
|   super(spellChecker, self).__init__() | ||||
|   log.debug("Creating the SpellChecker object. Dictionary: %s" % (dictionary,)) | ||||
|   self.active = True | ||||
|   try: | ||||
|    if config.app["app-settings"]["language"] == "system": | ||||
|     log.debug("Using the system language") | ||||
|     self.checker = SpellChecker(filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter]) | ||||
|    else: | ||||
|     log.debug("Using language: %s" % (languageHandler.getLanguage(),)) | ||||
|     self.checker = SpellChecker(languageHandler.getLanguage(), filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter]) | ||||
|    self.checker.set_text(text) | ||||
|   except DictNotFoundError: | ||||
|    log.exception("Dictionary for language %s not found." % (dictionary,)) | ||||
|    wx_ui.dict_not_found_error() | ||||
|    self.active = False | ||||
|   if self.active == True: | ||||
|    log.debug("Creating dialog...") | ||||
|    self.dialog = wx_ui.spellCheckerDialog() | ||||
|    widgetUtils.connect_event(self.dialog.ignore, widgetUtils.BUTTON_PRESSED, self.ignore) | ||||
|    widgetUtils.connect_event(self.dialog.ignoreAll, widgetUtils.BUTTON_PRESSED, self.ignoreAll) | ||||
|    widgetUtils.connect_event(self.dialog.replace, widgetUtils.BUTTON_PRESSED, self.replace) | ||||
|    widgetUtils.connect_event(self.dialog.replaceAll, widgetUtils.BUTTON_PRESSED, self.replaceAll) | ||||
|    self.check() | ||||
|    self.dialog.get_response() | ||||
|    self.fixed_text = self.checker.get_text() | ||||
|  | ||||
|  def check(self): | ||||
|   try: | ||||
|    self.checker.next() | ||||
|    textToSay = _(u"Misspelled word: %s") % (self.checker.word,) | ||||
|    context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10)) | ||||
|    self.dialog.set_title(textToSay) | ||||
|    output.speak(textToSay) | ||||
|    self.dialog.set_word_and_suggestions(word=self.checker.word, context=context, suggestions=self.checker.suggest()) | ||||
|   except StopIteration: | ||||
|    log.debug("Process finished.") | ||||
|    wx_ui.finished() | ||||
|    self.dialog.Destroy() | ||||
| #  except AttributeError: | ||||
| #   pass | ||||
|  | ||||
|  def ignore(self, ev): | ||||
|   self.check() | ||||
|  | ||||
|  def ignoreAll(self, ev): | ||||
|   self.checker.ignore_always(word=self.checker.word) | ||||
|   self.check() | ||||
|  | ||||
|  def replace(self, ev): | ||||
|   self.checker.replace(self.dialog.get_selected_suggestion()) | ||||
|   self.check() | ||||
|  | ||||
|  def replaceAll(self, ev): | ||||
|   self.checker.replace_always(self.dialog.get_selected_suggestion()) | ||||
|   self.check() | ||||
							
								
								
									
										15
									
								
								src/extra/SpellChecker/twitterFilter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/extra/SpellChecker/twitterFilter.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import re | ||||
| from enchant.tokenize import Filter | ||||
|  | ||||
| class TwitterFilter(Filter): | ||||
|     """Filter skipping over twitter usernames and hashtags. | ||||
|     This filter skips any words matching the following regular expression: | ||||
|     ^[#@](\S){1, }$ | ||||
|     That is, any words that resemble users and hashtags. | ||||
|     """ | ||||
|     _pattern = re.compile(r"^[#@](\S){1,}$") | ||||
|     def _skip(self,word): | ||||
|         if self._pattern.match(word): | ||||
|             return True | ||||
|         return False | ||||
							
								
								
									
										79
									
								
								src/extra/SpellChecker/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/extra/SpellChecker/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| ############################################################ | ||||
| #    Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net> | ||||
| #        | ||||
| #    This program is free software: you can redistribute it and/or modify | ||||
| #    it under the terms of the GNU General Public License as published by | ||||
| #    the Free Software Foundation, either version 2 of the License, or | ||||
| #    (at your option) any later version. | ||||
| # | ||||
| #    This program is distributed in the hope that it will be useful, | ||||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #    GNU General Public License for more details. | ||||
| # | ||||
| #    You should have received a copy of the GNU General Public License | ||||
| #    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ############################################################ | ||||
| import wx | ||||
| import application | ||||
| class spellCheckerDialog(wx.Dialog): | ||||
|  def __init__(self): | ||||
|   super(spellCheckerDialog, self).__init__(None, 1) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   word = wx.StaticText(panel, -1, _(u"Misspelled word")) | ||||
|   self.word = wx.TextCtrl(panel, -1) | ||||
|   wordBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   wordBox.Add(word, 0, wx.ALL, 5) | ||||
|   wordBox.Add(self.word, 0, wx.ALL, 5) | ||||
|   context = wx.StaticText(panel, -1, _(u"Context")) | ||||
|   self.context = wx.TextCtrl(panel, -1) | ||||
|   contextBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   contextBox.Add(context, 0, wx.ALL, 5) | ||||
|   contextBox.Add(self.context, 0, wx.ALL, 5) | ||||
|   suggest = wx.StaticText(panel, -1, _(u"Suggestions")) | ||||
|   self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE) | ||||
|   suggestionsBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   suggestionsBox.Add(suggest, 0, wx.ALL, 5) | ||||
|   suggestionsBox.Add(self.suggestions, 0, wx.ALL, 5) | ||||
|   self.ignore = wx.Button(panel, -1, _(u"Ignore")) | ||||
|   self.ignoreAll = wx.Button(panel, -1, _(u"Ignore all")) | ||||
|   self.replace = wx.Button(panel, -1, _(u"Replace")) | ||||
|   self.replaceAll = wx.Button(panel, -1, _(u"Replace all")) | ||||
|   close = wx.Button(panel, wx.ID_CANCEL) | ||||
|   btnBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   btnBox.Add(self.ignore, 0, wx.ALL, 5) | ||||
|   btnBox.Add(self.ignoreAll, 0, wx.ALL, 5) | ||||
|   btnBox.Add(self.replace, 0, wx.ALL, 5) | ||||
|   btnBox.Add(self.replaceAll, 0, wx.ALL, 5) | ||||
|   btnBox.Add(close, 0, wx.ALL, 5) | ||||
|   sizer.Add(wordBox, 0, wx.ALL, 5) | ||||
|   sizer.Add(contextBox, 0, wx.ALL, 5) | ||||
|   sizer.Add(suggestionsBox, 0, wx.ALL, 5) | ||||
|   sizer.Add(btnBox, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  | ||||
|  def get_response(self): | ||||
|   return self.ShowModal() | ||||
|  | ||||
|  def set_title(self, title): | ||||
|   return self.SetTitle(title) | ||||
|  | ||||
|  def set_word_and_suggestions(self, word, context, suggestions): | ||||
|   self.word.SetValue(word) | ||||
|   self.context.ChangeValue(context) | ||||
|   self.suggestions.Set(suggestions) | ||||
|   self.suggestions.SetFocus() | ||||
|  | ||||
|  def get_selected_suggestion(self): | ||||
|   return self.suggestions.GetStringSelection() | ||||
|  | ||||
| def dict_not_found_error(): | ||||
|  wx.MessageDialog(None, _(u"An error has occurred. There are no dictionaries available for the selected language in {0}").format(application.name,), _(u"Error"), wx.ICON_ERROR).ShowModal() | ||||
|  | ||||
| def finished(): | ||||
|  wx.MessageDialog(None, _(u"Spell check complete."), application.name, style=wx.OK).ShowModal() | ||||
							
								
								
									
										0
									
								
								src/extra/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/extra/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2
									
								
								src/extra/autocompletionUsers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/extra/autocompletionUsers/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import completion, settings | ||||
							
								
								
									
										47
									
								
								src/extra/autocompletionUsers/completion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/extra/autocompletionUsers/completion.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import output | ||||
| import storage | ||||
| import wx_menu | ||||
|  | ||||
| class autocompletionUsers(object): | ||||
|  def __init__(self, window, session_id): | ||||
|   super(autocompletionUsers, self).__init__() | ||||
|   self.window = window | ||||
|   self.db = storage.storage(session_id) | ||||
|  | ||||
|  def show_menu(self, mode="tweet"): | ||||
|   position = self.window.get_position() | ||||
|   if mode == "tweet": | ||||
|    text = self.window.get_text() | ||||
|    text = text[:position] | ||||
|    try: | ||||
|     pattern = text.split()[-1] | ||||
|    except IndexError: | ||||
|     output.speak(_(u"You have to start writing")) | ||||
|     return | ||||
|    if pattern.startswith("@") == True: | ||||
|     menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode) | ||||
|     users = self.db.get_users(pattern[1:]) | ||||
|     if len(users) > 0: | ||||
|      menu.append_options(users) | ||||
|      self.window.popup_menu(menu) | ||||
|      menu.destroy() | ||||
|     else: | ||||
|      output.speak(_(u"There are no results in your users database")) | ||||
|    else: | ||||
|     output.speak(_(u"Autocompletion only works for users.")) | ||||
|   elif mode == "dm": | ||||
|    text = self.window.get_user() | ||||
|    try: | ||||
|     pattern = text.split()[-1] | ||||
|    except IndexError: | ||||
|     output.speak(_(u"You have to start writing")) | ||||
|     return | ||||
|    menu = wx_menu.menu(self.window.cb, pattern, mode=mode) | ||||
|    users = self.db.get_users(pattern) | ||||
|    if len(users) > 0: | ||||
|     menu.append_options(users) | ||||
|     self.window.popup_menu(menu) | ||||
|     menu.destroy() | ||||
|    else: | ||||
|     output.speak(_(u"There are no results in your users database")) | ||||
							
								
								
									
										43
									
								
								src/extra/autocompletionUsers/manage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/extra/autocompletionUsers/manage.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import storage | ||||
| import widgetUtils | ||||
| import wx_manage | ||||
| from wxUI import commonMessageDialogs | ||||
|  | ||||
| class autocompletionManage(object): | ||||
|  def __init__(self, session): | ||||
|   super(autocompletionManage, self).__init__() | ||||
|   self.session = session | ||||
|   self.dialog = wx_manage.autocompletionManageDialog() | ||||
|   self.database = storage.storage(self.session.session_id) | ||||
|   self.users = self.database.get_all_users() | ||||
|   self.dialog.put_users(self.users) | ||||
|   widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user) | ||||
|   widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user) | ||||
|   self.dialog.get_response() | ||||
|  | ||||
|  def update_list(self): | ||||
|   item = self.dialog.users.get_selected() | ||||
|   self.dialog.users.clear() | ||||
|   self.users = self.database.get_all_users() | ||||
|   self.dialog.put_users(self.users) | ||||
|   self.dialog.users.select_item(item) | ||||
|  | ||||
|  def add_user(self, *args, **kwargs): | ||||
|   usr = self.dialog.get_user() | ||||
|   if usr == False: | ||||
|    return | ||||
|   try: | ||||
|    data = self.session.twitter.twitter.show_user(screen_name=usr) | ||||
|   except: | ||||
|    self.dialog.show_invalid_user_error() | ||||
|    return | ||||
|   self.database.set_user(data["screen_name"], data["name"], 0) | ||||
|   self.update_list() | ||||
|  | ||||
|  def remove_user(self, ev): | ||||
|   if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES: | ||||
|    item = self.dialog.users.get_selected() | ||||
|    user = self.users[item] | ||||
|    self.database.remove_user(user[0]) | ||||
|    self.update_list() | ||||
							
								
								
									
										59
									
								
								src/extra/autocompletionUsers/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/extra/autocompletionUsers/settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import storage | ||||
| import widgetUtils | ||||
| import wx_settings | ||||
| import manage | ||||
| import output | ||||
| from mysc.thread_utils import call_threaded | ||||
|  | ||||
| class autocompletionSettings(object): | ||||
|  def __init__(self, config, buffer, window): | ||||
|   super(autocompletionSettings, self).__init__() | ||||
|   self.config = config | ||||
|   self.buffer = buffer | ||||
|   self.window = window | ||||
|   self.dialog = wx_settings.autocompletionSettingsDialog() | ||||
|   self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"]) | ||||
|   self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"]) | ||||
|   widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list) | ||||
|   if self.dialog.get_response() == widgetUtils.OK: | ||||
|    call_threaded(self.add_users_to_database) | ||||
|  | ||||
|  def add_users_to_database(self): | ||||
|   self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer") | ||||
|   self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer") | ||||
|   output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes.")) | ||||
|   database = storage.storage(self.buffer.session.session_id) | ||||
|   if self.dialog.get("followers_buffer") == True: | ||||
|    buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"]) | ||||
|    for i in buffer.session.db[buffer.name]["items"]: | ||||
|     database.set_user(i["screen_name"], i["name"], 1) | ||||
|   else: | ||||
|    database.remove_by_buffer(1) | ||||
|   if self.dialog.get("friends_buffer") == True: | ||||
|    buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"]) | ||||
|    for i in buffer.session.db[buffer.name]["items"]: | ||||
|     database.set_user(i["screen_name"], i["name"], 2) | ||||
|   else: | ||||
|    database.remove_by_buffer(2) | ||||
|   wx_settings.show_success_dialog() | ||||
|   self.dialog.destroy() | ||||
|    | ||||
|  def view_list(self, ev): | ||||
|   q = manage.autocompletionManage(self.buffer.session) | ||||
|  | ||||
|  | ||||
| def execute_at_startup(window, buffer, config): | ||||
|  database = storage.storage(buffer.session.session_id) | ||||
|  if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True: | ||||
|   buffer = window.search_buffer("followers", config["twitter"]["user_name"]) | ||||
|   for i in buffer.session.db[buffer.name]: | ||||
|    database.set_user(i["screen_name"], i["name"], 1) | ||||
|  else: | ||||
|   database.remove_by_buffer(1) | ||||
|  if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True: | ||||
|   buffer = window.search_buffer("friends", config["twitter"]["user_name"]) | ||||
|   for i in buffer.session.db[buffer.name]: | ||||
|    database.set_user(i["screen_name"], i["name"], 2) | ||||
|  else: | ||||
|   database.remove_by_buffer(2)   | ||||
							
								
								
									
										52
									
								
								src/extra/autocompletionUsers/storage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/extra/autocompletionUsers/storage.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import sqlite3, paths | ||||
|  | ||||
| class storage(object): | ||||
|  def __init__(self, session_id): | ||||
|   self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (session_id))) | ||||
|   self.cursor = self.connection.cursor() | ||||
|   if self.table_exist("users") == False: | ||||
|    self.create_table() | ||||
|  | ||||
|  def table_exist(self, table): | ||||
|   ask = self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % (table)) | ||||
|   answer = ask.fetchone() | ||||
|   if answer == None: | ||||
|    return False | ||||
|   else: | ||||
|    return True | ||||
|  | ||||
|  def get_all_users(self): | ||||
|   self.cursor.execute("""select * from users""") | ||||
|   return self.cursor.fetchall() | ||||
|  | ||||
|  def get_users(self, term): | ||||
|   self.cursor.execute("""SELECT * FROM users WHERE user LIKE ?""", ('{}%'.format(term),)) | ||||
|   return self.cursor.fetchall() | ||||
|  | ||||
|  def set_user(self, screen_name, user_name, from_a_buffer): | ||||
|   self.cursor.execute("""insert or ignore into users values(?, ?, ?)""", (screen_name, user_name, from_a_buffer)) | ||||
|   self.connection.commit() | ||||
|  | ||||
|  def remove_user(self, user): | ||||
|   self.cursor.execute("""DELETE FROM users WHERE user = ?""", (user,)) | ||||
|   self.connection.commit() | ||||
|   return self.cursor.fetchone() | ||||
|  | ||||
|  def remove_by_buffer(self, bufferType): | ||||
|   """ Removes all users saved on a buffer. BufferType is 0 for no buffer, 1 for friends and 2 for followers""" | ||||
|   self.cursor.execute("""DELETE  FROM users WHERE from_a_buffer = ?""", (bufferType,)) | ||||
|   self.connection.commit() | ||||
|   return self.cursor.fetchone() | ||||
|  | ||||
|  def create_table(self): | ||||
|   self.cursor.execute(""" | ||||
|   create table users( | ||||
| user TEXT UNIQUE, | ||||
| name TEXT, | ||||
| from_a_buffer INTEGER | ||||
| )""") | ||||
|  | ||||
|  def __del__(self): | ||||
|   self.cursor.close() | ||||
|   self.connection.close() | ||||
							
								
								
									
										43
									
								
								src/extra/autocompletionUsers/wx_manage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/extra/autocompletionUsers/wx_manage.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
| from multiplatform_widgets import widgets | ||||
| import application | ||||
| class autocompletionManageDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self): | ||||
|   super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   label = wx.StaticText(panel, -1, _(u"Editing {0} users database").format(application.name,)) | ||||
|   self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT) | ||||
|   sizer.Add(label, 0, wx.ALL, 5) | ||||
|   sizer.Add(self.users.list, 0, wx.ALL, 5) | ||||
|   self.add = wx.Button(panel, -1, _(u"Add user")) | ||||
|   self.remove = wx.Button(panel, -1, _(u"Remove user")) | ||||
|   optionsBox = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   optionsBox.Add(self.add, 0, wx.ALL, 5) | ||||
|   optionsBox.Add(self.remove, 0, wx.ALL, 5) | ||||
|   sizer.Add(optionsBox, 0, wx.ALL, 5) | ||||
|   ok = wx.Button(panel, wx.ID_OK) | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL) | ||||
|   sizerBtn = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   sizerBtn.Add(ok, 0, wx.ALL, 5) | ||||
|   sizer.Add(cancel, 0, wx.ALL, 5) | ||||
|   sizer.Add(sizerBtn, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
|  def put_users(self, users): | ||||
|   for i in users: | ||||
|    j = [i[0], i[1]] | ||||
|    self.users.insert_item(False, *j) | ||||
|  | ||||
|  def get_user(self): | ||||
|   usr = False | ||||
|   userDlg = wx.TextEntryDialog(None, _(u"Twitter username"), _(u"Add user to database")) | ||||
|   if userDlg.ShowModal() == wx.ID_OK: | ||||
|    usr = userDlg.GetValue() | ||||
|   return usr | ||||
|  | ||||
|  def show_invalid_user_error(self): | ||||
|   wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error!"), wx.ICON_ERROR).ShowModal() | ||||
							
								
								
									
										25
									
								
								src/extra/autocompletionUsers/wx_menu.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/extra/autocompletionUsers/wx_menu.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
|  | ||||
| class menu(wx.Menu): | ||||
|  def __init__(self, window, pattern, mode): | ||||
|   super(menu, self).__init__() | ||||
|   self.window = window | ||||
|   self.pattern = pattern | ||||
|   self.mode = mode | ||||
|  | ||||
|  def append_options(self, options): | ||||
|   for i in options: | ||||
|    item = wx.MenuItem(self, wx.NewId(), "%s (@%s)" % (i[1], i[0])) | ||||
|    self.AppendItem(item) | ||||
|    self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item) | ||||
|  | ||||
|  def select_text(self, ev, text): | ||||
|   if self.mode == "tweet": | ||||
|    self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" ")) | ||||
|   elif self.mode == "dm": | ||||
|    self.window.SetValue(self.window.GetValue().replace(self.pattern, text)) | ||||
|   self.window.SetInsertionPointEnd() | ||||
|  | ||||
|  def destroy(self): | ||||
|   self.Destroy() | ||||
							
								
								
									
										27
									
								
								src/extra/autocompletionUsers/wx_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/extra/autocompletionUsers/wx_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
| import application | ||||
|  | ||||
| class autocompletionSettingsDialog(widgetUtils.BaseDialog): | ||||
|  def __init__(self): | ||||
|   super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings")) | ||||
|   panel = wx.Panel(self) | ||||
|   sizer = wx.BoxSizer(wx.VERTICAL) | ||||
|   self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer")) | ||||
|   self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer")) | ||||
|   sizer.Add(self.followers_buffer, 0, wx.ALL, 5) | ||||
|   sizer.Add(self.friends_buffer, 0, wx.ALL, 5) | ||||
|   self.viewList = wx.Button(panel, -1, _(u"Manage database...")) | ||||
|   sizer.Add(self.viewList, 0, wx.ALL, 5) | ||||
|   ok = wx.Button(panel, wx.ID_OK) | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL) | ||||
|   sizerBtn = wx.BoxSizer(wx.HORIZONTAL) | ||||
|   sizerBtn.Add(ok, 0, wx.ALL, 5) | ||||
|   sizer.Add(cancel, 0, wx.ALL, 5) | ||||
|   sizer.Add(sizerBtn, 0, wx.ALL, 5) | ||||
|   panel.SetSizer(sizer) | ||||
|   self.SetClientSize(sizer.CalcMin()) | ||||
|  | ||||
| def show_success_dialog(): | ||||
|  wx.MessageDialog(None, _(u"{0}'s database of users has been updated.").format(application.name,), _(u"Done"), wx.OK).ShowModal() | ||||
							
								
								
									
										6
									
								
								src/extra/translator/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/extra/translator/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import translator | ||||
| import platform | ||||
| if platform.system() == "Windows": | ||||
|  import wx_ui as gui | ||||
|   | ||||
							
								
								
									
										251
									
								
								src/extra/translator/translator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								src/extra/translator/translator.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| # encoding: utf-8 | ||||
| # | ||||
| # Copyright (C) 2013 Mesar Hameed <mhameed@src.gnome.org> | ||||
| # This file is covered by the GNU General Public License. | ||||
|  | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
| import threading | ||||
| from time import sleep | ||||
| from random import randint | ||||
| import logging | ||||
| log = logging.getLogger("translator") | ||||
| import urllib2 | ||||
|  | ||||
| # 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"), | ||||
|   "sq": _(u"Albanian"), | ||||
|   "am": _(u"Amharic"), | ||||
|   "ar": _(u"Arabic"), | ||||
|   "hy": _(u"Armenian"), | ||||
|   "az": _(u"Azerbaijani"), | ||||
|   "eu": _(u"Basque"), | ||||
|   "be": _(u"Belarusian"), | ||||
|   "bn": _(u"Bengali"), | ||||
|   "bh": _(u"Bihari"), | ||||
|   "bg": _(u"Bulgarian"), | ||||
|   "my": _(u"Burmese"), | ||||
|   "ca": _(u"Catalan"), | ||||
|   "chr": _(u"Cherokee"), | ||||
|   "zh": _(u"Chinese"), | ||||
|   "zh-CN": _(u"Chinese_simplified"), | ||||
|   "zh-TW": _(u"Chinese_traditional"), | ||||
|   "hr": _(u"Croatian"), | ||||
|   "cs": _(u"Czech"), | ||||
|   "da": _(u"Danish"), | ||||
|   "dv": _(u"Dhivehi"), | ||||
|   "nl": _(u"Dutch"), | ||||
|   "en": _(u"English"), | ||||
|   "eo": _(u"Esperanto"), | ||||
|   "et": _(u"Estonian"), | ||||
|   "tl": _(u"Filipino"), | ||||
|   "fi": _(u"Finnish"), | ||||
|   "fr": _(u"French"), | ||||
|   "gl": _(u"Galician"), | ||||
|   "ka": _(u"Georgian"), | ||||
|   "de": _(u"German"), | ||||
|   "el": _(u"Greek"), | ||||
|   "gn": _(u"Guarani"), | ||||
|   "gu": _(u"Gujarati"), | ||||
|   "iw": _(u"Hebrew"), | ||||
|   "hi": _(u"Hindi"), | ||||
|   "hu": _(u"Hungarian"), | ||||
|   "is": _(u"Icelandic"), | ||||
|   "id": _(u"Indonesian"), | ||||
|   "iu": _(u"Inuktitut"), | ||||
|   "ga": _(u"Irish"), | ||||
|   "it": _(u"Italian"), | ||||
|   "ja": _(u"Japanese"), | ||||
|   "kn": _(u"Kannada"), | ||||
|   "kk": _(u"Kazakh"), | ||||
|   "km": _(u"Khmer"), | ||||
|   "ko": _(u"Korean"), | ||||
|   "ku": _(u"Kurdish"), | ||||
|   "ky": _(u"Kyrgyz"), | ||||
|   "lo": _(u"Laothian"), | ||||
|   "lv": _(u"Latvian"), | ||||
|   "lt": _(u"Lithuanian"), | ||||
|   "mk": _(u"Macedonian"), | ||||
|   "ms": _(u"Malay"), | ||||
|   "ml": _(u"Malayalam"), | ||||
|   "mt": _(u"Maltese"), | ||||
|   "mr": _(u"Marathi"), | ||||
|   "mn": _(u"Mongolian"), | ||||
|   "ne": _(u"Nepali"), | ||||
|   "no": _(u"Norwegian"), | ||||
|   "or": _(u"Oriya"), | ||||
|   "ps": _(u"Pashto"), | ||||
|   "fa": _(u"Persian"), | ||||
|   "pl": _(u"Polish"), | ||||
|   "pt-PT": _(u"Portuguese"), | ||||
|   "pa": _(u"Punjabi"), | ||||
|   "ro": _(u"Romanian"), | ||||
|   "ru": _(u"Russian"), | ||||
|   "sa": _(u"Sanskrit"), | ||||
|   "sr": _(u"Serbian"), | ||||
|   "sd": _(u"Sindhi"), | ||||
|   "si": _(u"Sinhalese"), | ||||
|   "sk": _(u"Slovak"), | ||||
|   "sl": _(u"Slovenian"), | ||||
|   "es": _(u"Spanish"), | ||||
|   "sw": _(u"Swahili"), | ||||
|   "sv": _(u"Swedish"), | ||||
|   "tg": _(u"Tajik"), | ||||
|   "ta": _(u"Tamil"), | ||||
|   "tl": _(u"Tagalog"), | ||||
|   "te": _(u"Telugu"), | ||||
|   "th": _(u"Thai"), | ||||
|   "bo": _(u"Tibetan"), | ||||
|   "tr": _(u"Turkish"), | ||||
|   "uk": _(u"Ukrainian"), | ||||
|   "ur": _(u"Urdu"), | ||||
|   "uz": _(u"Uzbek"), | ||||
|   "ug": _(u"Uighur"), | ||||
|   "vi": _(u"Vietnamese"), | ||||
|   "cy": _(u"Welsh"), | ||||
|   "yi": _(u"Yiddish") | ||||
| } | ||||
|  | ||||
| def available_languages(): | ||||
|     l = languages.keys() | ||||
|     d = languages.values() | ||||
|     l.insert(0, '') | ||||
|     d.insert(0, _(u"autodetect")) | ||||
|     return sorted(zip(l, d)) | ||||
							
								
								
									
										45
									
								
								src/extra/translator/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/extra/translator/wx_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| ############################################################ | ||||
| #    Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net> | ||||
| #        | ||||
| #    This program is free software: you can redistribute it and/or modify | ||||
| #    it under the terms of the GNU General Public License as published by | ||||
| #    the Free Software Foundation, either version 2 of the License, or | ||||
| #    (at your option) any later version. | ||||
| # | ||||
| #    This program is distributed in the hope that it will be useful, | ||||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #    GNU General Public License for more details. | ||||
| # | ||||
| #    You should have received a copy of the GNU General Public License | ||||
| #    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ############################################################ | ||||
| import translator | ||||
| import wx | ||||
| from widgetUtils  import BaseDialog | ||||
|  | ||||
| class translateDialog(BaseDialog): | ||||
|  def __init__(self): | ||||
|   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) | ||||
|   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) | ||||
|   ok.SetDefault() | ||||
|   cancel = wx.Button(panel, wx.ID_CANCEL) | ||||
|   self.SetEscapeId(wx.ID_CANCEL) | ||||
|  | ||||
|  def get(self, control): | ||||
|   return getattr(self, control).GetSelection() | ||||
							
								
								
									
										13
									
								
								src/keys/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/keys/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| keyring = None | ||||
|  | ||||
| def setup(): | ||||
| 	global keyring | ||||
| 	if keyring == None: | ||||
| 		keyring = Keyring() | ||||
|  | ||||
| class Keyring(object): | ||||
|  | ||||
| 	def get_api_key(self): | ||||
| 		return "5093442" | ||||
							
								
								
									
										7
									
								
								src/libloader/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/libloader/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| from .libloader import * | ||||
|  | ||||
| __version__ = 0.1 | ||||
| __author__ = 'Christopher Toth <q@q-continuum.net>' | ||||
| __doc__ = """ | ||||
| Quickly and easily load shared libraries from various platforms. Also includes a libloader.com module for loading com modules on Windows. | ||||
| """ | ||||
							
								
								
									
										21
									
								
								src/libloader/com.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/libloader/com.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| from pywintypes import com_error | ||||
| from win32com.client import gencache | ||||
|  | ||||
| def prepare_gencache(): | ||||
|  gencache.is_readonly = False | ||||
|  gencache.GetGeneratePath() | ||||
|  | ||||
|  | ||||
|  | ||||
| def load_com(*names): | ||||
|  result = None | ||||
|  for name in names: | ||||
|   try: | ||||
|    result = gencache.EnsureDispatch(name) | ||||
|    break | ||||
|   except com_error: | ||||
|    continue | ||||
|  if result is None: | ||||
|   raise com_error("Unable to load any of the provided com objects.") | ||||
|  return result | ||||
|  | ||||
							
								
								
									
										56
									
								
								src/libloader/libloader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/libloader/libloader.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| import ctypes | ||||
| import collections | ||||
| import platform | ||||
| import os | ||||
|  | ||||
| TYPES = { | ||||
|  'Linux': { | ||||
|   'loader': ctypes.CDLL, | ||||
|   'functype': ctypes.CFUNCTYPE, | ||||
|   'prefix': 'lib', | ||||
|   'extension': '.so' | ||||
|  }, | ||||
|  'Darwin': { | ||||
|   'loader': ctypes.CDLL, | ||||
|   'functype': ctypes.CFUNCTYPE, | ||||
|   'prefix': 'lib', | ||||
|   'extension': '.dylib' | ||||
|  }, | ||||
| } | ||||
| if platform.system() == 'Windows': | ||||
|  TYPES['Windows'] = { | ||||
|   'loader': ctypes.WinDLL, | ||||
|   'functype': ctypes.WINFUNCTYPE, | ||||
|   'prefix': "", | ||||
|   'extension': '.dll' | ||||
|  } | ||||
|  | ||||
| class LibraryLoadError(OSError): pass | ||||
|  | ||||
| def load_library(library, x86_path='.', x64_path='.', *args, **kwargs): | ||||
|  lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path) | ||||
|  loaded = _do_load(lib, *args, **kwargs) | ||||
|  if loaded is not None: | ||||
|   return loaded | ||||
|  raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, path)) | ||||
|  | ||||
| def _do_load(file, *args, **kwargs): | ||||
|  loader = TYPES[platform.system()]['loader']  | ||||
|  return loader(file, *args, **kwargs) | ||||
|  | ||||
| def find_library_path(libname, x86_path='.', x64_path='.'): | ||||
|  libname = '%s%s' % (TYPES[platform.system()]['prefix'], libname) | ||||
|  if platform.architecture()[0] == '64bit': | ||||
|   path = os.path.join(x64_path, libname) | ||||
|  else: | ||||
|   path = os.path.join(x86_path, libname) | ||||
|  ext = get_library_extension() | ||||
|  path = '%s%s' % (path, ext) | ||||
|  return os.path.abspath(path) | ||||
|  | ||||
|    | ||||
| def get_functype(): | ||||
|  return TYPES[platform.system()]['functype'] | ||||
|  | ||||
| def get_library_extension(): | ||||
|  return TYPES[platform.system()]['extension'] | ||||
							
								
								
									
										37
									
								
								src/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/main.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import platform | ||||
| import languageHandler | ||||
| import widgetUtils | ||||
| import paths | ||||
| import config | ||||
| import output | ||||
| import logging | ||||
| import keys | ||||
| import application | ||||
| from mysc.thread_utils import call_threaded | ||||
|  | ||||
| log = logging.getLogger("main") | ||||
|  | ||||
| def setup(): | ||||
| 	log.debug("Starting Socializer %s" % (application.version,)) | ||||
| 	config.setup() | ||||
| 	log.debug("Using %s %s" % (platform.system(), platform.architecture()[0])) | ||||
| 	log.debug("Application path is %s" % (paths.app_path(),)) | ||||
| 	log.debug("config path  is %s" % (paths.config_path(),)) | ||||
| 	output.setup() | ||||
| 	languageHandler.setLanguage("system") | ||||
| 	keys.setup() | ||||
| 	from controller import mainController | ||||
| 	from sessionmanager import sessionManager | ||||
| 	app = widgetUtils.mainLoopObject() | ||||
| 	sm = sessionManager.sessionManagerController() | ||||
| 	sm.fill_list() | ||||
| 	if len(sm.sessions) == 0: sm.show() | ||||
| 	else: | ||||
| 		sm.do_ok() | ||||
| 	del sm | ||||
| 	r = mainController.Controller() | ||||
| 	call_threaded(r.login) | ||||
| 	app.run() | ||||
|  | ||||
| setup() | ||||
							
								
								
									
										0
									
								
								src/mysc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/mysc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								src/mysc/localization.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/mysc/localization.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import os | ||||
| import languageHandler | ||||
| import logging | ||||
| log = logging.getLogger("mysc.localization") | ||||
|  | ||||
| def get(rootFolder): | ||||
|  log.debug("Getting documentation folder. RootFolder: %s" % (rootFolder,)) | ||||
|  defaultLocale = languageHandler.curLang | ||||
|  if len(defaultLocale) > 2: | ||||
|   defaultLocale = defaultLocale[:2] | ||||
|  log.debug("Locale: %s" % (defaultLocale,)) | ||||
|  if os.path.exists(rootFolder+"/"+defaultLocale): | ||||
|   return defaultLocale | ||||
|  else: | ||||
|   log.debug("The folder does not exist, using the English folder...") | ||||
|   return "en" | ||||
|  | ||||
							
								
								
									
										36
									
								
								src/mysc/repeating_timer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/mysc/repeating_timer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| import threading | ||||
| import logging | ||||
| log = logging.getLogger("mysc.repeating_timer") | ||||
|  | ||||
| class RepeatingTimer(threading.Thread): | ||||
|  """Call a function after a specified number of seconds, it will then repeat again after the specified number of seconds | ||||
|     Note: If the function provided takes time to execute, this time is NOT taken from the next wait period | ||||
|  | ||||
|  t = RepeatingTimer(30.0, f, args=[], kwargs={}) | ||||
|  t.start() | ||||
|  t.cancel() # stop the timer's actions | ||||
|  """ | ||||
|  | ||||
|  def __init__(self, interval, function, daemon=True, *args, **kwargs): | ||||
|   threading.Thread.__init__(self) | ||||
|   self.daemon = daemon | ||||
|   self.interval = float(interval) | ||||
|   self.function = function | ||||
|   self.args = args | ||||
|   self.kwargs = kwargs | ||||
|   self.finished = threading.Event() | ||||
|  | ||||
|  def cancel(self): | ||||
|   """Stop the timer if it hasn't finished yet""" | ||||
|   log.debug("Stopping repeater for %s" % (self.function,)) | ||||
|   self.finished.set() | ||||
|  stop = cancel | ||||
|  | ||||
|  def run(self): | ||||
|   while not self.finished.is_set(): | ||||
|    self.finished.wait(self.interval) | ||||
|    if not self.finished.is_set():  #In case someone has canceled while waiting | ||||
|     try: | ||||
|      self.function(*self.args, **self.kwargs) | ||||
|     except: | ||||
|      log.exception("Execution failed. Function: %r args: %r and kwargs: %r" % (self.function, self.args, self.kwargs)) | ||||
							
								
								
									
										11
									
								
								src/mysc/restart.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/mysc/restart.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| # -*- coding: cp1252 | ||||
| import sys, os | ||||
|  | ||||
| def restart_program(): | ||||
|  """ Function that restarts the application if is executed.""" | ||||
|  args = sys.argv[:] | ||||
|  if not hasattr(sys, "frozen"): | ||||
|   args.insert(0, sys.executable) | ||||
|  if sys.platform == 'win32': | ||||
|   args = ['"%s"' % arg for arg in args] | ||||
|  os.execv(sys.executable, args) | ||||
							
								
								
									
										14
									
								
								src/mysc/thread_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/mysc/thread_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import threading | ||||
|  | ||||
| def call_threaded(func, *args, **kwargs): | ||||
|  #Call the given function in a daemonized thread and return the thread. | ||||
|  def new_func(*a, **k): | ||||
| #  try: | ||||
|   func(*a, **k) | ||||
| #  except: | ||||
| #   pass | ||||
|  thread = threading.Thread(target=new_func, args=args, kwargs=kwargs) | ||||
|  thread.daemon = True | ||||
|  thread.start() | ||||
|  return thread | ||||
							
								
								
									
										24
									
								
								src/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/output.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| # *- coding: utf-8 -*- | ||||
| import logging as original_logging | ||||
| logging = original_logging.getLogger('core.output') | ||||
|  | ||||
| from accessible_output2 import outputs | ||||
| import sys | ||||
|  | ||||
| speaker = None | ||||
|  | ||||
| def speak(text, interrupt=0): | ||||
| 	global speaker | ||||
| 	if not speaker: | ||||
| 		setup() | ||||
| 	speaker.speak(text, interrupt) | ||||
| 	speaker.braille(text) | ||||
|  | ||||
| def setup (): | ||||
| 	global speaker | ||||
| 	logging.debug("Initializing output subsystem.") | ||||
| 	try: | ||||
| 		speaker = outputs.auto.Auto() | ||||
| 	except: | ||||
| 		return logging.exception("Output: Error during initialization.") | ||||
|  | ||||
							
								
								
									
										67
									
								
								src/paths.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/paths.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import platform | ||||
| import os | ||||
| import sys | ||||
| import logging | ||||
| from platform_utils import paths as paths_ | ||||
|  | ||||
| from functools import wraps | ||||
|  | ||||
| mode = "portable" | ||||
| directory = None | ||||
|  | ||||
| log = logging.getLogger("paths") | ||||
|  | ||||
| def merge_paths(func): | ||||
| 	@wraps(func) | ||||
| 	def merge_paths_wrapper(*a): | ||||
| 		return unicode(os.path.join(func(), *a)) | ||||
| 	return merge_paths_wrapper | ||||
|  | ||||
| @merge_paths | ||||
| def app_path(): | ||||
| 	return paths_.app_path() | ||||
|  | ||||
| @merge_paths | ||||
| def config_path(): | ||||
| 	global mode, directory | ||||
| 	if mode == "portable": | ||||
| 		if directory != None: path = os.path.join(directory, "config") | ||||
| 		elif directory == None: path = app_path(u"config") | ||||
| 	elif mode == "installed": | ||||
| 		path = data_path("config") | ||||
| 	if not os.path.exists(path): | ||||
| 		log.debug("%s path does not exist, creating..." % (path,)) | ||||
| 		os.mkdir(path) | ||||
| 	return path | ||||
|  | ||||
| @merge_paths | ||||
| def logs_path(): | ||||
| 	global mode, directory | ||||
| 	if mode == "portable": | ||||
| 		if directory != None: path = os.path.join(directory, "logs") | ||||
| 		elif directory == None: path = app_path(u"logs") | ||||
| 	elif mode == "installed": | ||||
| 		path = data_path("logs") | ||||
| 	if not os.path.exists(path): | ||||
| 		log.debug("%s path does not exist, creating..." % (path,)) | ||||
| 		os.mkdir(path) | ||||
| 	return path | ||||
|  | ||||
| @merge_paths | ||||
| def data_path(app_name='TW blue'): | ||||
| 	if platform.system() == "Windows": | ||||
| 		data_path = os.path.join(os.getenv("AppData"), app_name) | ||||
| 	else: | ||||
| 		data_path = os.path.join(os.environ['HOME'], ".%s" % app_name) | ||||
| 	if not os.path.exists(data_path): | ||||
| 		os.mkdir(data_path) | ||||
| 	return data_path | ||||
|  | ||||
| @merge_paths | ||||
| def locale_path(): | ||||
| 	return app_path(u"locales") | ||||
|  | ||||
| @merge_paths | ||||
| def sound_path(): | ||||
| 	return app_path(u"sounds") | ||||
							
								
								
									
										0
									
								
								src/platform_utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/platform_utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								src/platform_utils/autostart/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/platform_utils/autostart/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										41
									
								
								src/platform_utils/autostart/windows.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/platform_utils/autostart/windows.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| import _winreg | ||||
| import os | ||||
| import sys | ||||
| from platform_utils import paths | ||||
|  | ||||
| RUN_REGKEY = ur"SOFTWARE\Microsoft\Windows\CurrentVersion\Run" | ||||
|  | ||||
| def is_installed(app_subkey): | ||||
|  """Checks if the currently running copy is installed or portable variant. Requires the name of the application subkey found under the uninstall section in Windows registry.""" | ||||
|  | ||||
|  try: | ||||
|   key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s" % app_subkey) | ||||
|   inst_dir = _winreg.QueryValueEx(key,"InstallLocation")[0] | ||||
|  except WindowsError: | ||||
|   return False | ||||
|  _winreg.CloseKey(key) | ||||
|  try: | ||||
|   return os.stat(inst_dir) == os.stat(paths.app_path()) | ||||
|  except WindowsError: | ||||
|   return False | ||||
|  | ||||
| def getAutoStart(app_name): | ||||
|  """Queries if the automatic startup should be set for the application or not, depending on it's current state.""" | ||||
|  | ||||
|  try: | ||||
|   key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, RUN_REGKEY) | ||||
|   val = _winreg.QueryValueEx(key, unicode(app_name))[0] | ||||
|   return os.stat(val) == os.stat(sys.argv[0]) | ||||
|  except (WindowsError, OSError): | ||||
|   return False | ||||
|  | ||||
| def setAutoStart(app_name, enable=True): | ||||
|  """Configures automatic startup for the application, if the enable argument is set to True. If set to False, deletes the application AutoStart value.""" | ||||
|  | ||||
|  if getAutoStart(app_name) == enable: | ||||
|   return | ||||
|  key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, _winreg.KEY_WRITE) | ||||
|  if enable: | ||||
|   _winreg.SetValueEx(key, unicode(app_name), None, _winreg.REG_SZ, sys.argv[0]) | ||||
|  else: | ||||
|   _winreg.DeleteValue(key, unicode(app_name)) | ||||
							
								
								
									
										16
									
								
								src/platform_utils/blackhole.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/platform_utils/blackhole.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # Replacement for py2exe distributed module | ||||
| # Avoids the use of the standard py2exe console. | ||||
| # Just import this file and it should go away | ||||
|  | ||||
| import sys | ||||
| if hasattr(sys,"frozen"): # true only if we are running as a py2exe app | ||||
|  class Blackhole(object): | ||||
|   def write(self,text): | ||||
|    pass | ||||
|   def flush(self): | ||||
|    pass | ||||
|  sys.stdout = Blackhole() | ||||
|  sys.stderr = Blackhole() | ||||
|  del Blackhole | ||||
|  del sys | ||||
|  | ||||
							
								
								
									
										51
									
								
								src/platform_utils/libloader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/platform_utils/libloader.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import ctypes | ||||
| import collections | ||||
| import platform | ||||
| import os | ||||
|  | ||||
| TYPES = { | ||||
|  'Linux': { | ||||
|   'loader': ctypes.CDLL, | ||||
|   'functype': ctypes.CFUNCTYPE, | ||||
|   'prefix': 'lib', | ||||
|   'extension': '.so' | ||||
|  }, | ||||
|  'Darwin': { | ||||
|   'loader': ctypes.CDLL, | ||||
|   'functype': ctypes.CFUNCTYPE, | ||||
|   'prefix': 'lib', | ||||
|   'extension': '.dylib' | ||||
|  }, | ||||
| } | ||||
| if platform.system() == 'Windows': | ||||
|  TYPES['Windows'] = { | ||||
|   'loader': ctypes.WinDLL, | ||||
|   'functype': ctypes.WINFUNCTYPE, | ||||
|   'prefix': "", | ||||
|   'extension': '.dll' | ||||
|  } | ||||
|  | ||||
| class LibraryLoadError(Exception): pass | ||||
|  | ||||
| def load_library(library, x86_path='.', x64_path='.', *args, **kwargs): | ||||
|  lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path) | ||||
|  loaded = _do_load(lib, *args, **kwargs) | ||||
|  if loaded is not None: | ||||
|   return loaded | ||||
|  raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, path)) | ||||
|  | ||||
| def _do_load(file, *args, **kwargs): | ||||
|  loader = TYPES[platform.system()]['loader']  | ||||
|  return loader(file, *args, **kwargs) | ||||
|  | ||||
| def find_library_path(libname, x86_path='.', x64_path='.'): | ||||
|  libname = '%s%s' % (TYPES[platform.system()]['prefix'], libname) | ||||
|  if platform.machine() == 'x86_64': | ||||
|   path = os.path.join(x64_path, libname) | ||||
|  else: | ||||
|   path = os.path.join(x86_path, libname) | ||||
|  ext = TYPES[platform.system()]['extension'] | ||||
|  return '%s%s' % (path, ext) | ||||
|    | ||||
| def get_functype(): | ||||
|  return TYPES[platform.system()]['functype'] | ||||
							
								
								
									
										114
									
								
								src/platform_utils/paths.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/platform_utils/paths.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| import inspect | ||||
| import platform | ||||
| import os | ||||
| import subprocess | ||||
| import sys | ||||
| import string | ||||
| import unicodedata | ||||
|  | ||||
|  | ||||
| def app_data_path(app_name=None): | ||||
|  """Cross-platform method for determining where to put application data.""" | ||||
|  """Requires the name of the application""" | ||||
|  plat = platform.system() | ||||
|  if plat == 'Windows': | ||||
|   import winpaths | ||||
|   path = winpaths.get_appdata() | ||||
|  elif plat == 'Darwin': | ||||
|   path = os.path.join(os.path.expanduser('~'), 'Library', 'Application Support') | ||||
|  elif plat == 'Linux': | ||||
|   path = os.path.expanduser('~') | ||||
|   app_name = '.%s' % app_name.replace(' ', '_') | ||||
|  return os.path.join(path, app_name) | ||||
|  | ||||
| def prepare_app_data_path(app_name): | ||||
|  """Creates the application's data directory, given its name.""" | ||||
|  dir = app_data_path(app_name) | ||||
|  return ensure_path(dir) | ||||
|  | ||||
| def embedded_data_path(): | ||||
|  if platform.system() == 'Darwin' and is_frozen(): | ||||
|   return os.path.abspath(os.path.join(executable_directory(), '..', 'Resources')) | ||||
|  return app_path() | ||||
|  | ||||
| def is_frozen(): | ||||
|  """Return a bool indicating if application is compressed""" | ||||
|  import imp | ||||
|  return hasattr(sys, 'frozen') or imp.is_frozen("__main__") | ||||
|  | ||||
| def get_executable(): | ||||
|  """Returns the full executable path/name if frozen, or the full path/name of the main module if not.""" | ||||
|  if is_frozen(): | ||||
|   if platform.system() != 'Darwin': | ||||
|    return sys.executable | ||||
| #On darwin, sys.executable points to python. We want the full path to the exe we ran. | ||||
|   exedir = os.path.abspath(os.path.dirname(sys.executable)) | ||||
|   items = os.listdir(exedir) | ||||
|   items.remove('python') | ||||
|   return os.path.join(exedir, items[0]) | ||||
|  #Not frozen | ||||
|  try: | ||||
|   import __main__ | ||||
|   return os.path.abspath(__main__.__file__) | ||||
|  except AttributeError: | ||||
|   return sys.argv[0] | ||||
|  | ||||
| def get_module(level=2): | ||||
|  """Hacky method for deriving the caller of this function's module.""" | ||||
|  return inspect.getmodule(inspect.stack()[level][0]).__file__ | ||||
|  | ||||
| def executable_directory(): | ||||
|  """Always determine the directory of the executable, even when run with py2exe or otherwise frozen""" | ||||
|  executable = get_executable() | ||||
|  path = os.path.abspath(os.path.dirname(executable)) | ||||
|  return path | ||||
|  | ||||
| def app_path(): | ||||
|  """Return the root of the application's directory""" | ||||
|  path = executable_directory() | ||||
|  if is_frozen() and platform.system() == 'Darwin': | ||||
|   path = os.path.abspath(os.path.join(path, '..', '..')) | ||||
|  return path | ||||
|  | ||||
| def module_path(level=2): | ||||
|  return os.path.abspath(os.path.dirname(get_module(level))) | ||||
|  | ||||
| def documents_path(): | ||||
|  """On windows, returns the path to My Documents. On OSX, returns the user's Documents folder. For anything else, returns the user's home directory.""" | ||||
|  plat = platform.system() | ||||
|  if plat == 'Windows': | ||||
|   import winpaths | ||||
|   path = winpaths.get_my_documents() | ||||
|  elif plat == 'Darwin': | ||||
|   path = os.path.join(os.path.expanduser('~'), 'Documents') | ||||
|  else: | ||||
|   path = os.path.expanduser('~') | ||||
|  return path | ||||
|  | ||||
| def safe_filename(filename): | ||||
|  """Given a filename, returns a safe version with no characters that would not work on different platforms.""" | ||||
|  SAFE_FILE_CHARS = "'-_.()[]{}!@#$%^&+=`~ " | ||||
|  filename = unicode(filename) | ||||
|  new_filename = ''.join(c for c in filename if c in SAFE_FILE_CHARS or c.isalnum()) | ||||
|  #Windows doesn't like directory names ending in space, macs consider filenames beginning with a dot as hidden, and windows removes dots at the ends of filenames. | ||||
|  return new_filename.strip(' .') | ||||
|  | ||||
| def ensure_path(path): | ||||
|  if not os.path.exists(path): | ||||
|   os.makedirs(path) | ||||
|  return path | ||||
|  | ||||
| def start_file(path): | ||||
|  if platform.system() == 'Windows': | ||||
|   os.startfile(path) | ||||
|  else: | ||||
|   subprocess.Popen(['open', path]) | ||||
|  | ||||
| def get_applications_path(): | ||||
|  """Return the directory where applications are commonly installed on the system.""" | ||||
|  plat = platform.system() | ||||
|  if plat == 'Windows': | ||||
|   import winpaths | ||||
|   return winpaths.get_program_files() | ||||
|  elif plat == 'Darwin': | ||||
|   return '/Applications' | ||||
							
								
								
									
										27
									
								
								src/platform_utils/process.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/platform_utils/process.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| import platform | ||||
| import ctypes | ||||
| import os | ||||
| import signal | ||||
|  | ||||
|  | ||||
| def kill_windows_process(pid): | ||||
|  PROCESS_TERMINATE = 1 | ||||
|  SYNCHRONIZE=1048576 | ||||
|  handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, False, pid) | ||||
|  ctypes.windll.kernel32.TerminateProcess(handle, -1) | ||||
|  ctypes.windll.kernel32.WaitForSingleObject(handle, 1000) | ||||
|  ctypes.windll.kernel32.CloseHandle(handle) | ||||
|  | ||||
| def kill_unix_process(pid): | ||||
|  try: | ||||
|   os.kill(pid, signal.SIGKILL) | ||||
|  except OSError: | ||||
|   pass | ||||
|  | ||||
| def kill_process(pid): | ||||
|  if pid < 0: | ||||
|   return | ||||
|  if platform.system() == 'Windows': | ||||
|   kill_windows_process(pid) | ||||
|  else: | ||||
|   kill_unix_process(pid) | ||||
							
								
								
									
										0
									
								
								src/platform_utils/shell_integration/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/platform_utils/shell_integration/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										10
									
								
								src/platform_utils/shell_integration/windows.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/platform_utils/shell_integration/windows.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import _winreg | ||||
|  | ||||
| SHELL_REGKEY = ur"Directory\shell" | ||||
|  | ||||
| def context_menu_integrate(item_key_name, item_display_text, item_command): | ||||
|  app_menu_key = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, SHELL_REGKEY, 0, _winreg.KEY_WRITE) | ||||
|  menu_item_key = _winreg.CreateKey(app_menu_key, item_key_name) | ||||
|  _winreg.SetValueEx(menu_item_key, None, None, _winreg.REG_SZ, item_display_text) | ||||
|  item_command_key = _winreg.CreateKey(menu_item_key, 'command') | ||||
|  _winreg.SetValueEx(item_command_key, None, None, _winreg.REG_SZ, item_command) | ||||
							
								
								
									
										9
									
								
								src/platform_utils/web_browser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/platform_utils/web_browser.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| import platform | ||||
| import webbrowser | ||||
|  | ||||
| def open(url): | ||||
|  if platform.system() == 'Windows': | ||||
|   browser = webbrowser.get('windows-default') | ||||
|  else: | ||||
|   browser = webbrowser | ||||
|  browser.open_new_tab(url) | ||||
							
								
								
									
										6
									
								
								src/session.defaults
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/session.defaults
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| [vk] | ||||
| user = string(default="") | ||||
| password = string(default="") | ||||
| token = string(default="") | ||||
| [general] | ||||
| reverse_timelines = boolean(default=False) | ||||
							
								
								
									
										0
									
								
								src/sessionmanager/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/sessionmanager/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										46
									
								
								src/sessionmanager/config_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/sessionmanager/config_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| from UserDict import UserDict | ||||
| from configobj import ConfigObj, ParseError | ||||
| from validate import Validator, VdtValueError | ||||
| import os | ||||
|  | ||||
| class ConfigurationResetException(Exception): | ||||
|  pass | ||||
|  | ||||
| class Configuration (UserDict): | ||||
|  | ||||
| 	def __init__ (self, file=None, spec=None, *args, **kwargs): | ||||
| 		self.file = file | ||||
| 		self.spec = spec | ||||
| 		self.validator = Validator() | ||||
| 		self.setup_config(file=file, spec=spec) | ||||
| 		self.validated = self.config.validate(self.validator, copy=True) | ||||
| 		if self.validated: | ||||
| 			self.write() | ||||
| 		UserDict.__init__(self, self.config) | ||||
|  | ||||
| 	def setup_config (self, file, spec): | ||||
| 		spec = ConfigObj(spec, list_values=False, encoding="utf-8") | ||||
| 		try: | ||||
| 			self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True, encoding="utf-8") | ||||
| 		except ParseError: | ||||
| 			os.remove(file) | ||||
| 			self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True) | ||||
| 			raise ConfigurationResetException | ||||
|  | ||||
| 	def __getitem__ (self, *args, **kwargs): | ||||
| 		return dict(self.config).__getitem__(*args, **kwargs) | ||||
|  | ||||
| 	def __setitem__ (self, *args, **kwargs): | ||||
| 		self.config.__setitem__(*args, **kwargs) | ||||
| 		UserDict.__setitem__(self, *args, **kwargs) | ||||
|  | ||||
| 	def write (self): | ||||
| 		if hasattr(self.config, 'write'): | ||||
| 			self.config.write() | ||||
|  | ||||
| class SessionConfiguration (Configuration): | ||||
| 	def setup_config (self, file, spec): | ||||
| 		#No infile required. | ||||
| 		spec = ConfigObj(spec, list_values=False) | ||||
| 		self.config = ConfigObj(configspec=spec, stringify=True) | ||||
							
								
								
									
										226
									
								
								src/sessionmanager/session.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								src/sessionmanager/session.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,226 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import arrow | ||||
| import languageHandler | ||||
| import paths | ||||
| import vkSessionHandler | ||||
| import logging | ||||
| import utils | ||||
| from config_utils import Configuration, ConfigurationResetException | ||||
| log = logging.getLogger("vk.session") | ||||
|  | ||||
| sessions = {} | ||||
|  | ||||
| def add_attachment(attachment): | ||||
| 	""" Adds information about the attachment files in posts. It only adds the text, I mean, no attachment file is added here. | ||||
| 	This will produce a result like 'Title of a web page: http://url.xxx', etc.""" | ||||
| 	msg = u"" | ||||
| 	if attachment["type"] == "link": | ||||
| 		msg = u"{0}: {1}".format(attachment["link"]["title"], attachment["link"]["url"]) | ||||
| 	elif attachment["type"] == "photo": | ||||
| 		msg = attachment["photo"]["text"] | ||||
| 		if msg == "": | ||||
| 			return "photo with no description available" | ||||
| 	elif attachment["type"] == "video": | ||||
| 		msg = u"video: {0}".format(attachment["video"]["title"],) | ||||
| 	return msg | ||||
|  | ||||
| def add_text(status): | ||||
| 	""" This shorts the text to 140 characters for displaying it in the list control.""" | ||||
| 	message = "" | ||||
| 	if status.has_key("text"): | ||||
| 		if len(status["text"]) < 140: | ||||
| 			message = status["text"] | ||||
| 		else: | ||||
| 			message = status["text"][:139] | ||||
| 	return message | ||||
|  | ||||
| def compose_new(status, session): | ||||
| 	""" This method is used to compose an item of the news feed.""" | ||||
| 	user = session.get_user_name(status["source_id"]) | ||||
| 	message = "" | ||||
| 	original_date = arrow.get(status["date"]) | ||||
| 	created_at = original_date.humanize(locale=languageHandler.getLanguage()) | ||||
| 	if status["type"] == "post": | ||||
| 		message += add_text(status) | ||||
| 		if status.has_key("attachment") and len(status["attachment"]) > 0: | ||||
| 			message += add_attachment(status["attachment"]) | ||||
| 		if message == "": | ||||
| 			message = "no description available" | ||||
| 	elif status["type"] == "audio": | ||||
| 		message = u"{0} has posted an audio: {1}".format(user, u", ".join(compose_audio(status["audio"][1], session)),) | ||||
| 	elif status["type"] == "friend": | ||||
| 		ids = "" | ||||
| 		for i in status["friends"][1:]: | ||||
| 			ids = ids + "{0}, ".format(i["uid"]) | ||||
| 		users = session.vk.client.users.get(user_ids=ids, fields="uid, first_name, last_name") | ||||
| 		msg_users = u"" | ||||
| 		for i in users: | ||||
| 			msg_users = msg_users + u"{0} {1}, ".format(i["first_name"], i["last_name"]) | ||||
| 		message = u"{0} hadded friends: {1}".format(user, msg_users) | ||||
| 	else: | ||||
| 		if status["type"] != "post": print status["type"] | ||||
| 	return [user, message, created_at] | ||||
|  | ||||
| def compose_status(status, session): | ||||
| #	print status.keys() | ||||
| 	user = session.get_user_name(status["from_id"]) | ||||
| 	message = "" | ||||
| #	user = status["copy_owner_id"] | ||||
| 	original_date = arrow.get(status["date"]) | ||||
| 	created_at = original_date.humanize(locale=languageHandler.getLanguage()) | ||||
| #	created_at = str(status["date"]) | ||||
| 	if status["post_type"] == "post": | ||||
| 		message += add_text(status) | ||||
| 	if status.has_key("attachment") and len(status["attachment"]) > 0: | ||||
| 		message += add_attachment(status["attachment"]) | ||||
| 		if message == "": | ||||
| 			message = "no description available" | ||||
| 	return [user, message, created_at] | ||||
|  | ||||
| def compose_audio(audio, session): | ||||
| #	print audio | ||||
| 	return [audio["title"], audio["artist"], utils.seconds_to_string(audio["duration"])] | ||||
|  | ||||
| class vkSession(object): | ||||
|  | ||||
| 	def order_buffer(self, name, data, field): | ||||
|  | ||||
| 		""" Put the new items on the local database. Useful for cursored buffers | ||||
| 		name str: The name for the buffer stored in the dictionary. | ||||
| 		data list: A list with items and some information about cursors. | ||||
| 		returns the number of items that has been added in this execution""" | ||||
|  | ||||
| 		num = 0 | ||||
| 		if self.db.has_key(name) == False: | ||||
| 			self.db[name] = {} | ||||
| 			self.db[name]["items"] = [] | ||||
| 		for i in data: | ||||
| #			print i.keys() | ||||
| #			print i.keys() | ||||
| #			print i["type"] | ||||
| #			if i.has_key(field) and find_item(i[field], self.db[name]["items"], field) == None: | ||||
| 			if i.has_key("type") and i["type"] == "wall_photo": continue | ||||
| 			if i not in self.db[name]["items"]: | ||||
| 				if self.settings["general"]["reverse_timelines"] == False: self.db[name]["items"].append(i) | ||||
| 				else: self.db[name]["items"].insert(0, i) | ||||
| 				num = num+1 | ||||
| 		return num | ||||
|  | ||||
| 	def __init__(self, session_id): | ||||
| 		self.session_id = session_id | ||||
| 		self.logged = False | ||||
| 		self.settings = None | ||||
| 		self.vk = vkSessionHandler.vkObject() | ||||
| 		self.db = {} | ||||
| 		self.db["users"] = {} | ||||
| 		self.db["groups"] = {} | ||||
|  | ||||
| 	@property | ||||
| 	def is_logged(self): | ||||
| 		return self.logged | ||||
|  | ||||
| 	def get_configuration(self): | ||||
|  | ||||
| 		""" Gets settings for a session.""" | ||||
|   | ||||
| 		file_ = "%s/session.conf" % (self.session_id,) | ||||
| #  try: | ||||
| 		log.debug("Creating config file %s" % (file_,)) | ||||
| 		self.settings = Configuration(paths.config_path(file_), paths.app_path("session.defaults")) | ||||
| #  except: | ||||
| #   log.exception("The session configuration has failed.") | ||||
|  | ||||
| 	def login(self): | ||||
| 		""" Login  using  credentials from settings. | ||||
| 		if the user account isn't authorised, it needs to call self.authorise() before login.""" | ||||
|  | ||||
| 		if self.settings["vk"]["token"] != None: | ||||
| 			self.vk.login_access_token(self.settings["vk"]["token"]) | ||||
| 			self.logged = True | ||||
| 			log.debug("Logged.") | ||||
| 		else: | ||||
| 			self.logged = False | ||||
| 			raise Exceptions.RequireCredentialsSessionError | ||||
|  | ||||
| 	def authorise(self): | ||||
| 		if self.logged == True: | ||||
| 			raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.") | ||||
| 		else: | ||||
| 			self.vk.login(self.settings["vk"]["user"], self.settings["vk"]["password"]) | ||||
| 			self.settings["vk"]["token"] = self.vk.client._session.access_token | ||||
|  | ||||
| 	def post_wall_status(self, message, *args, **kwargs): | ||||
| 		response = self.vk.client.wall.post(message=message, *args, **kwargs) | ||||
| #		print response | ||||
|  | ||||
| 	def get_newsfeed(self, name="newsfeed", no_next=True, endpoint="", *args, **kwargs): | ||||
| 		data = getattr(self.vk.client.newsfeed, "get")(*args, **kwargs) | ||||
| #			print data | ||||
| 		if data != None: | ||||
| #			try: | ||||
| #			num = self.order_buffer(name, data[1:]) | ||||
| #			except: | ||||
| 			num = self.order_buffer(name, data["items"][:-1], "post_id") | ||||
| 			ids = "" | ||||
| 			gids = "" | ||||
| 			for i in data["items"][:-1]: | ||||
| 				if i.has_key("source_id"): | ||||
| 					if i["source_id"] > 0: | ||||
| 						if str(i["source_id"]) not in ids: ids += "{0},".format(i["source_id"]) | ||||
| 					else: | ||||
| 						if str(i["source_id"]) not in gids: gids += "{0},".format(abs(i["source_id"])) | ||||
| 			self.get_users(ids, gids) | ||||
| 			return num | ||||
|  | ||||
| 	def get_page(self, name="", no_next=True, endpoint="", *args, **kwargs): | ||||
| 		data = None | ||||
| 		full_list = False | ||||
| 		if kwargs.has_key("parent_endpoint"): | ||||
| 			p = kwargs["parent_endpoint"] | ||||
| 			kwargs.pop("parent_endpoint") | ||||
| 		if kwargs.has_key("full_list"): | ||||
| 			print kwargs | ||||
| 			full_list = True | ||||
| 			kwargs.pop("full_list") | ||||
| 		if kwargs.has_key("identifier"): | ||||
| 			identifier = kwargs["identifier"] | ||||
| 			kwargs.pop("identifier") | ||||
| 		p = getattr(self.vk.client, p) | ||||
| 		data = getattr(p, endpoint)(*args, **kwargs) | ||||
| #			print data | ||||
| 		if data != None: | ||||
| #			try: | ||||
| 			if full_list == False: | ||||
| 				num = self.order_buffer(name, data[1:], identifier) | ||||
| 			else: | ||||
| 				num = self.order_buffer(name, data, identifier) | ||||
| #			except: | ||||
| #				num = self.order_buffer(name, data["items"][:-1]) | ||||
| 			ids = "" | ||||
| 			for i in data[1:]: | ||||
| 				if i.has_key("from_id"): | ||||
| 					if str(i["from_id"]) not in ids: ids += "{0},".format(i["from_id"]) | ||||
| 			self.get_users(ids) | ||||
| 			return num | ||||
|  | ||||
| 	def get_user_name(self, user_id): | ||||
| 		if user_id > 0: | ||||
| 			if self.db["users"].has_key(user_id): | ||||
| 				return self.db["users"][user_id] | ||||
| 			else: | ||||
| 				return "no specified user" | ||||
| 		else: | ||||
| 			if self.db["groups"].has_key(abs(user_id)): | ||||
| 				return self.db["groups"][abs(user_id)] | ||||
| 			else: | ||||
| 				return "no specified community" | ||||
|  | ||||
| 	def get_users(self, user_ids=None, group_ids=None): | ||||
| 		if user_ids != None: | ||||
| 			u = self.vk.client.users.get(user_ids=user_ids, fields="uid, first_name, last_name") | ||||
| 			for i in u: | ||||
| 				self.db["users"][i["uid"]] = u"{0} {1}".format(i["first_name"], i["last_name"]) | ||||
| 		if group_ids != None: | ||||
| 			g = self.vk.client.groups.getById(group_ids=group_ids, fields="name") | ||||
| 			for i in g: | ||||
| 				self.db["groups"][i["gid"]] = i["name"] | ||||
							
								
								
									
										84
									
								
								src/sessionmanager/sessionManager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/sessionmanager/sessionManager.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import os | ||||
| import shutil | ||||
| import widgetUtils | ||||
| import wxUI as view | ||||
| import paths | ||||
| import time | ||||
| import os | ||||
| import logging | ||||
| import session | ||||
| from config_utils import Configuration | ||||
|  | ||||
| log = logging.getLogger("sessionmanager.sessionManager") | ||||
|  | ||||
| class sessionManagerController(object): | ||||
| 	def __init__(self): | ||||
| 		super(sessionManagerController, self).__init__() | ||||
| 		log.debug("Setting up the session manager.") | ||||
| 		self.view = view.sessionManagerWindow() | ||||
| 		widgetUtils.connect_event(self.view.new, widgetUtils.BUTTON_PRESSED, self.manage_new_account) | ||||
| 		widgetUtils.connect_event(self.view.remove, widgetUtils.BUTTON_PRESSED, self.remove) | ||||
| 		self.new_sessions = {} | ||||
| 		self.removed_sessions = [] | ||||
|  | ||||
| 	def fill_list(self): | ||||
| 		sessionsList = [] | ||||
| 		log.debug("Filling the sessions list.") | ||||
| 		self.sessions = [] | ||||
| 		for i in os.listdir(paths.config_path()): | ||||
| 			if os.path.isdir(paths.config_path(i)): | ||||
| 				log.debug("Adding session %s" % (i,)) | ||||
| 				strconfig = "%s/session.conf" % (paths.config_path(i)) | ||||
| 				config_test = Configuration(strconfig) | ||||
| 				name = config_test["vk"]["user"] | ||||
| 				sessionsList.append(name) | ||||
| 				self.sessions.append(i) | ||||
| 		self.view.fill_list(sessionsList) | ||||
|  | ||||
| 	def show(self): | ||||
| 		if self.view.get_response() == widgetUtils.OK: | ||||
| 			self.do_ok() | ||||
|  | ||||
| 	def do_ok(self): | ||||
| 		log.debug("Starting sessions...") | ||||
| 		for i in self.sessions: | ||||
| 			if session.sessions.has_key(i) == True: continue | ||||
| 			s = session.vkSession(i) | ||||
| 			s.get_configuration() | ||||
| 			session.sessions[i] = s | ||||
| 			self.new_sessions[i] = s | ||||
|  | ||||
| 	def manage_new_account(self, *args, **kwargs): | ||||
| 		if self.view.new_account_dialog() == widgetUtils.YES: | ||||
| 			location = (str(time.time())[-6:]) | ||||
| 			log.debug("Creating session in the %s path" % (location,)) | ||||
| 			s = session.vkSession(location) | ||||
| 			path = paths.config_path(location) | ||||
| 			if not os.path.exists(path): | ||||
| 				log.debug("Creating %s path" % (paths.config_path(path),)) | ||||
| 				os.mkdir(path) | ||||
| 			s.get_configuration() | ||||
| 			self.get_authorisation(s) | ||||
| 			self.sessions.append(location) | ||||
| 			self.view.add_new_session_to_list() | ||||
| #   except: | ||||
| #    log.exception("Error authorising the session") | ||||
| #    self.view.show_unauthorised_error() | ||||
| #    return | ||||
|  | ||||
| 	def remove(self, *args, **kwargs): | ||||
| 		if self.view.remove_account_dialog() == widgetUtils.YES: | ||||
| 			selected_account = self.sessions[self.view.get_selected()] | ||||
| 			self.view.remove_session(self.view.get_selected()) | ||||
| 			self.removed_sessions.append(selected_account) | ||||
| 			self.sessions.remove(selected_account) | ||||
| 			shutil.rmtree(path=paths.config_path(selected_account), ignore_errors=True) | ||||
|  | ||||
| 	def get_authorisation(self, c): | ||||
| 		dl = view.newSessionDialog() | ||||
| 		if dl.ShowModal() == widgetUtils.OK: | ||||
| 			c.settings["vk"]["user"] = dl.get_email() | ||||
| 			c.settings["vk"]["password"] = dl.get_password() | ||||
| 			c.authorise() | ||||
| 			c.settings.write() | ||||
							
								
								
									
										17
									
								
								src/sessionmanager/vkSessionHandler.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/sessionmanager/vkSessionHandler.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/usr/bin/python | ||||
| import keys | ||||
| from vk import API, AuthSession, Session | ||||
|  | ||||
| class vkObject(object): | ||||
|  | ||||
| 	def __init__(self): | ||||
| 		self.api_key = keys.keyring.get_api_key() | ||||
|  | ||||
| 	def login(self, user, password): | ||||
| 		s = AuthSession(app_id=self.api_key, user_login=user, user_password=password, scope="wall, notify, friends, photos, audio, video, docs, notes, pages, status, groups, messages, notifications, stats") | ||||
| 		self.client = API(s) | ||||
| 		self.client.account.getProfileInfo() | ||||
|  | ||||
| 	def login_access_token(self, token): | ||||
| 		s = Session(access_token=token) | ||||
| 		self.client = API(s) | ||||
							
								
								
									
										92
									
								
								src/sessionmanager/wxUI.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/sessionmanager/wxUI.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import wx | ||||
| import widgetUtils | ||||
|  | ||||
| class sessionManagerWindow(widgetUtils.BaseDialog): | ||||
| 	def __init__(self): | ||||
| 		super(sessionManagerWindow, self).__init__(parent=None, title="Session manager", size=wx.DefaultSize) | ||||
| 		panel = wx.Panel(self) | ||||
| 		sizer = wx.BoxSizer(wx.VERTICAL) | ||||
| 		label = wx.StaticText(panel, -1, u"Accounts", size=wx.DefaultSize) | ||||
| 		listSizer = wx.BoxSizer(wx.HORIZONTAL) | ||||
| 		self.list = widgetUtils.list(panel, u"Account", style=wx.LC_SINGLE_SEL|wx.LC_REPORT) | ||||
| 		listSizer.Add(label, 0, wx.ALL, 5) | ||||
| 		listSizer.Add(self.list.list, 0, wx.ALL, 5) | ||||
| 		sizer.Add(listSizer, 0, wx.ALL, 5) | ||||
| 		self.new = wx.Button(panel, -1, u"New account", size=wx.DefaultSize) | ||||
| 		self.remove = wx.Button(panel, -1, _(u"Remove account")) | ||||
| 		ok = wx.Button(panel, wx.ID_OK, size=wx.DefaultSize) | ||||
| 		ok.SetDefault() | ||||
| 		cancel = wx.Button(panel, wx.ID_CANCEL, size=wx.DefaultSize) | ||||
| 		buttons = wx.BoxSizer(wx.HORIZONTAL) | ||||
| 		buttons.Add(self.new, 0, wx.ALL, 5) | ||||
| 		buttons.Add(ok, 0, wx.ALL, 5) | ||||
| 		buttons.Add(cancel, 0, wx.ALL, 5) | ||||
| 		sizer.Add(buttons, 0, wx.ALL, 5) | ||||
| 		panel.SetSizer(sizer) | ||||
| 		min = sizer.CalcMin() | ||||
| 		self.SetClientSize(min) | ||||
|  | ||||
| 	def fill_list(self, sessionsList): | ||||
| 		for i in sessionsList: | ||||
| 			self.list.insert_item(False, i) | ||||
| 		if self.list.get_count() > 0: | ||||
| 			self.list.select_item(0) | ||||
| 		self.list.list.SetSize(self.list.list.GetBestSize()) | ||||
|  | ||||
| 	def ok(self, ev): | ||||
| 		if self.list.get_count() == 0: | ||||
| 			wx.MessageDialog(None, _(u"You need to configure an account."), _(u"Account Error"), wx.ICON_ERROR).ShowModal() | ||||
| 			return | ||||
| 		self.controller.do_ok() | ||||
| 		self.EndModal(wx.ID_OK) | ||||
|  | ||||
| 	def new_account_dialog(self): | ||||
| 		return wx.MessageDialog(self, _(u"The request for the required facebook authorization to continue will be opened on your browser. You only need to do it once. Would you like to autorhise a new account now?"), _(u"Authorisation"), wx.YES_NO).ShowModal() | ||||
|  | ||||
| 	def add_new_session_to_list(self): | ||||
| 		total = self.list.get_count() | ||||
| 		name = _(u"Authorised account %d") % (total+1) | ||||
| 		self.list.insert_item(False, name) | ||||
| 		if self.list.get_count() == 1: | ||||
| 			self.list.select_item(0) | ||||
|  | ||||
| 	def remove_account_dialog(self): | ||||
| 		return wx.MessageDialog(self, _(u"Do you really want delete this account?"), _(u"Remove account"), wx.YES_NO).ShowModal() | ||||
|  | ||||
| 	def get_selected(self): | ||||
| 		return self.list.get_selected() | ||||
|  | ||||
| 	def remove_session(self, sessionID): | ||||
| 		self.list.remove_item(sessionID) | ||||
|  | ||||
| class newSessionDialog(widgetUtils.BaseDialog): | ||||
| 	def __init__(self): | ||||
| 		super(newSessionDialog, self).__init__(parent=None, id=wx.NewId(), title=_(u"Authorise VK")) | ||||
| 		panel = wx.Panel(self) | ||||
| 		lbl1 = wx.StaticText(panel, -1, _(u"Email address")) | ||||
| 		self.email = wx.TextCtrl(panel, -1) | ||||
| 		lbl2 = wx.StaticText(panel, -1, _(u"Password")) | ||||
| 		self.passw = wx.TextCtrl(panel, -1, style=wx.TE_PASSWORD) | ||||
| 		sizer = wx.BoxSizer() | ||||
| 		b1 = wx.BoxSizer(wx.HORIZONTAL) | ||||
| 		b1.Add(lbl1, 0, wx.ALL, 5) | ||||
| 		b1.Add(self.email, 0, wx.ALL, 5) | ||||
| 		b2 = wx.BoxSizer(wx.HORIZONTAL) | ||||
| 		b2.Add(lbl2, 0, wx.ALL, 5) | ||||
| 		b2.Add(self.passw, 0, wx.ALL, 5) | ||||
| 		sizer.Add(b1, 0, wx.ALL, 5) | ||||
| 		sizer.Add(b2, 0, wx.ALL, 5) | ||||
| 		ok = wx.Button(panel, wx.ID_OK) | ||||
| 		cancel = wx.Button(panel, wx.ID_CANCEL) | ||||
| 		btnb = wx.BoxSizer(wx.HORIZONTAL) | ||||
| 		btnb.Add(ok, 0, wx.ALL, 5) | ||||
| 		btnb.Add(cancel, 0, wx.ALL, 5) | ||||
| 		sizer.Add(btnb, 0, wx.ALL, 5) | ||||
| 		panel.SetSizer(sizer) | ||||
|  | ||||
| 	def get_email(self): | ||||
| 		return self.email.GetValue() | ||||
|  | ||||
| 	def get_password(self): | ||||
| 		return self.passw.GetValue() | ||||
							
								
								
									
										27
									
								
								src/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| def seconds_to_string(seconds, precision=0): | ||||
| 	day = seconds // 86400 | ||||
| 	hour = seconds // 3600 | ||||
| 	min = (seconds // 60) % 60 | ||||
| 	sec = seconds - (hour * 3600) - (min * 60) | ||||
| 	sec_spec = "." + str(precision) + "f" | ||||
| 	sec_string = sec.__format__(sec_spec) | ||||
| 	string = "" | ||||
| 	if day == 1: | ||||
| 		string += _(u"%d day, ") % day | ||||
| 	elif day >= 2: | ||||
| 		string += _(u"%d days, ") % day | ||||
| 	if (hour == 1): | ||||
| 		string += _(u"%d hour, ") % hour | ||||
| 	elif (hour >= 2): | ||||
| 		string += _("%d hours, ") % hour | ||||
| 	if (min == 1): | ||||
| 		string += _(u"%d minute, ") % min | ||||
| 	elif (min >= 2): | ||||
| 		string += _(u"%d minutes, ") % min | ||||
| 	if sec >= 0 and sec <= 2: | ||||
| 		string += _(u"%s second") % sec_string | ||||
| 	else: | ||||
| 		string += _(u"%s seconds") % sec_string | ||||
| 	return string | ||||
							
								
								
									
										10
									
								
								src/vk/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/vk/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
|  | ||||
| from vk.api import logger | ||||
| from vk.api import Session, AuthSession, InteractiveSession, InteractiveAuthSession | ||||
| from vk.api import VERSION | ||||
| from vk.api import API | ||||
| import upload | ||||
|  | ||||
| __version__ = version = VERSION | ||||
|  | ||||
| # API = OAuthAPI | ||||
							
								
								
									
										177
									
								
								src/vk/api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/vk/api.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| # coding=utf8 | ||||
|  | ||||
| import logging | ||||
| import logging.config | ||||
|  | ||||
| from vk.logs import LOGGING_CONFIG | ||||
| from vk.utils import stringify_values, json_iter_parse, LoggingSession | ||||
| from vk.exceptions import VkAuthError, VkAPIMethodError, CAPTCHA_IS_NEEDED, AUTHORIZATION_FAILED | ||||
| from vk.mixins import AuthMixin, InteractiveMixin | ||||
|  | ||||
|  | ||||
| VERSION = '2.0a4' | ||||
|  | ||||
|  | ||||
| logging.config.dictConfig(LOGGING_CONFIG) | ||||
| logger = logging.getLogger('vk') | ||||
|  | ||||
|  | ||||
| class Session(object): | ||||
|     API_URL = 'https://api.vk.com/method/' | ||||
|  | ||||
|     def __init__(self, access_token=None): | ||||
|  | ||||
|         logger.debug('API.__init__(access_token=%(access_token)r)', {'access_token': access_token}) | ||||
|  | ||||
|         # self.api_version = api_version | ||||
|         # self.default_timeout = default_timeout | ||||
|         self.access_token = access_token | ||||
|         self.access_token_is_needed = False | ||||
|  | ||||
|         # self.requests_session = requests.Session() | ||||
|         self.requests_session = LoggingSession() | ||||
|         self.requests_session.headers['Accept'] = 'application/json' | ||||
|         self.requests_session.headers['Content-Type'] = 'application/x-www-form-urlencoded' | ||||
|  | ||||
|     @property | ||||
|     def access_token(self): | ||||
|         logger.debug('Check that we need new access token') | ||||
|         if self.access_token_is_needed: | ||||
|             logger.debug('We need new access token. Try to get it.') | ||||
|             self.access_token, self._access_token_expires_in = self.get_access_token() | ||||
|             logger.info('Got new access token') | ||||
|         logger.debug('access_token = %r, expires in %s', self.censored_access_token, self._access_token_expires_in) | ||||
|         return self._access_token | ||||
|  | ||||
|     @access_token.setter | ||||
|     def access_token(self, value): | ||||
|         self._access_token = value | ||||
|         self._access_token_expires_in = None | ||||
|         self.access_token_is_needed = not self._access_token | ||||
|  | ||||
|     @property | ||||
|     def censored_access_token(self): | ||||
|         if self._access_token: | ||||
|             return '{}***{}'.format(self._access_token[:4], self._access_token[-4:]) | ||||
|  | ||||
|     def get_user_login(self): | ||||
|         logger.debug('Do nothing to get user login') | ||||
|  | ||||
|     def get_access_token(self): | ||||
|         """ | ||||
|         Dummy method | ||||
|         """ | ||||
|         logger.debug('API.get_access_token()') | ||||
|         return self._access_token, self._access_token_expires_in | ||||
|  | ||||
|     def make_request(self, method_request, **method_kwargs): | ||||
|  | ||||
|         logger.debug('Prepare API Method request') | ||||
|  | ||||
|         response = self.send_api_request(method_request) | ||||
|         response.raise_for_status() | ||||
|  | ||||
|         # there are may be 2 dicts in one JSON | ||||
|         # for example: {'error': ...}{'response': ...} | ||||
|         errors = [] | ||||
|         error_codes = [] | ||||
|         for data in json_iter_parse(response.text): | ||||
|             if 'error' in data: | ||||
|                 error_data = data['error'] | ||||
|                 if error_data['error_code'] == CAPTCHA_IS_NEEDED: | ||||
|                     return self.on_captcha_is_needed(error_data, method_request) | ||||
|  | ||||
|                 error_codes.append(error_data['error_code']) | ||||
|                 errors.append(error_data) | ||||
|  | ||||
|             if 'response' in data: | ||||
|                 for error in errors: | ||||
|                     logger.warning(str(error)) | ||||
|  | ||||
|                 return data['response'] | ||||
|              | ||||
|         if AUTHORIZATION_FAILED in error_codes:  # invalid access token | ||||
|             logger.info('Authorization failed. Access token will be dropped') | ||||
|             self.access_token = None | ||||
|             return self.make_request(method_request) | ||||
|         else: | ||||
|             raise VkAPIMethodError(errors[0]) | ||||
|  | ||||
|     def send_api_request(self, request): | ||||
|         url = self.API_URL + request._method_name | ||||
|         method_args = request._api._method_default_args.copy() | ||||
|         method_args.update(stringify_values(request._method_args)) | ||||
|         if self.access_token: | ||||
|             method_args['access_token'] = self.access_token | ||||
|         timeout = request._api._timeout | ||||
|         response = self.requests_session.post(url, method_args, timeout=timeout) | ||||
|         return response | ||||
|  | ||||
|     def on_captcha_is_needed(self, error_data, method_request): | ||||
|         """ | ||||
|         Default behavior on CAPTCHA is to raise exception | ||||
|         Reload this in child | ||||
|         """ | ||||
|         raise VkAPIMethodError(error_data) | ||||
|      | ||||
|     def auth_code_is_needed(self, content, session): | ||||
|         """ | ||||
|         Default behavior on 2-AUTH CODE is to raise exception | ||||
|         Reload this in child | ||||
|         """            | ||||
|         raise VkAuthError('Authorization error (2-factor code is needed)') | ||||
|      | ||||
|     def auth_captcha_is_needed(self, content, session): | ||||
|         """ | ||||
|         Default behavior on CAPTCHA is to raise exception | ||||
|         Reload this in child | ||||
|         """               | ||||
|         raise VkAuthError('Authorization error (captcha)') | ||||
|      | ||||
|     def phone_number_is_needed(self, content, session): | ||||
|         """ | ||||
|         Default behavior on PHONE NUMBER is to raise exception | ||||
|         Reload this in child | ||||
|         """ | ||||
|         logger.error('Authorization error (phone number is needed)') | ||||
|         raise VkAuthError('Authorization error (phone number is needed)') | ||||
|  | ||||
|  | ||||
| class API(object): | ||||
|     def __init__(self, session, timeout=10, **method_default_args): | ||||
|         self._session = session | ||||
|         self._timeout = timeout | ||||
|         self._method_default_args = method_default_args | ||||
|  | ||||
|     def __getattr__(self, method_name): | ||||
|         return Request(self, method_name) | ||||
|  | ||||
|     def __call__(self, method_name, **method_kwargs): | ||||
|         return getattr(self, method_name)(**method_kwargs) | ||||
|  | ||||
|  | ||||
| class Request(object): | ||||
|     __slots__ = ('_api', '_method_name', '_method_args') | ||||
|  | ||||
|     def __init__(self, api, method_name): | ||||
|         self._api = api | ||||
|         self._method_name = method_name | ||||
|  | ||||
|     def __getattr__(self, method_name): | ||||
|         return Request(self._api, self._method_name + '.' + method_name) | ||||
|  | ||||
|     def __call__(self, **method_args): | ||||
|         self._method_args = method_args | ||||
|         return self._api._session.make_request(self) | ||||
|  | ||||
|  | ||||
| class AuthSession(AuthMixin, Session): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class InteractiveSession(InteractiveMixin, Session): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class InteractiveAuthSession(InteractiveMixin, AuthSession): | ||||
|     pass | ||||
							
								
								
									
										30
									
								
								src/vk/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/vk/exceptions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
|  | ||||
| # API Error Codes | ||||
| AUTHORIZATION_FAILED = 5    # Invalid access token | ||||
| CAPTCHA_IS_NEEDED = 14 | ||||
| ACCESS_DENIED = 15          # No access to call this method | ||||
|  | ||||
| class VkException(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class VkAuthError(VkException): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class VkAPIMethodError(VkException): | ||||
|     __slots__ = ['error', 'code', 'message', 'request_params', 'redirect_uri'] | ||||
|  | ||||
|     def __init__(self, error): | ||||
|         super(VkAPIMethodError, self).__init__() | ||||
|         self.error = error | ||||
|         self.code = error.get('error_code') | ||||
|         self.message = error.get('error_msg') | ||||
|         self.request_params = error.get('request_params') | ||||
|         self.redirect_uri = error.get('redirect_uri') | ||||
|  | ||||
|     def __str__(self): | ||||
|         error_message = '{self.code}. {self.message}. request_params = {self.request_params}'.format(self=self) | ||||
|         if self.redirect_uri: | ||||
|             error_message += ',\nredirect_uri = "{self.redirect_uri}"'.format(self=self) | ||||
|         return error_message | ||||
							
								
								
									
										26
									
								
								src/vk/logs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/vk/logs.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
|  | ||||
| import sys | ||||
|  | ||||
|  | ||||
| LOGGING_CONFIG = { | ||||
|     'version': 1, | ||||
|     'loggers': { | ||||
|         'vk': { | ||||
|             'level': 'INFO', | ||||
|             'handlers': ['vk-stdout'], | ||||
|             'propagate': False, | ||||
|             }, | ||||
|         }, | ||||
|     'handlers': { | ||||
|         'vk-stdout': { | ||||
|             'class': 'logging.StreamHandler', | ||||
|             'stream': sys.stdout, | ||||
|             'formatter': 'vk-verbose', | ||||
|         }, | ||||
|     }, | ||||
|     'formatters': { | ||||
|         'vk-verbose': { | ||||
|             'format': '%(asctime)s %(name) -5s %(module)s:%(lineno)d %(levelname)s: %(message)s', | ||||
|         }, | ||||
|     }, | ||||
| } | ||||
							
								
								
									
										215
									
								
								src/vk/mixins.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								src/vk/mixins.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| # coding=utf8 | ||||
|  | ||||
| import re | ||||
| import logging | ||||
|  | ||||
| import requests | ||||
|  | ||||
| from vk.exceptions import VkAuthError | ||||
| from vk.utils import urlparse, parse_qsl, raw_input, get_url_query, LoggingSession, get_form_action | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger('vk') | ||||
|  | ||||
|  | ||||
| class AuthMixin(object): | ||||
|     LOGIN_URL = 'https://m.vk.com' | ||||
|     # REDIRECT_URI = 'https://oauth.vk.com/blank.html' | ||||
|     AUTHORIZE_URL = 'https://oauth.vk.com/authorize' | ||||
|     CAPTCHA_URI = 'https://m.vk.com/captcha.php' | ||||
|  | ||||
|     def __init__(self, app_id=None, user_login='', user_password='', scope='offline', **kwargs): | ||||
|         logger.debug('AuthMixin.__init__(app_id=%(app_id)r, user_login=%(user_login)r, user_password=%(user_password)r, **kwargs=%(kwargs)s)', | ||||
|             dict(app_id=app_id, user_login=user_login, user_password=user_password, kwargs=kwargs)) | ||||
|  | ||||
|         super(AuthMixin, self).__init__(**kwargs) | ||||
|  | ||||
|         self.app_id = app_id | ||||
|         self.user_login = user_login | ||||
|         self.user_password = user_password | ||||
|         self.scope = scope | ||||
|  | ||||
|     @property | ||||
|     def user_login(self): | ||||
|         if not self._user_login: | ||||
|             self._user_login = self.get_user_login() | ||||
|         return self._user_login | ||||
|  | ||||
|     @user_login.setter | ||||
|     def user_login(self, value): | ||||
|         self._user_login = value | ||||
|  | ||||
|     def get_user_login(self): | ||||
|         return self._user_login | ||||
|  | ||||
|     @property | ||||
|     def user_password(self): | ||||
|         if not self._user_password: | ||||
|             self._user_password = self.get_user_password() | ||||
|         return self._user_password | ||||
|  | ||||
|     @user_password.setter | ||||
|     def user_password(self, value): | ||||
|         self._user_password = value | ||||
|  | ||||
|     def get_user_password(self): | ||||
|         return self._user_password | ||||
|      | ||||
|     def get_access_token(self): | ||||
|         """ | ||||
|         Get access token using app id and user login and password. | ||||
|         """ | ||||
|         logger.debug('AuthMixin.get_access_token()') | ||||
|  | ||||
|         auth_session = LoggingSession() | ||||
|         with auth_session as self.auth_session: | ||||
|             self.auth_session = auth_session | ||||
|             self.login() | ||||
|             auth_response_url_query = self.oauth2_authorization() | ||||
|  | ||||
|         if 'access_token' in auth_response_url_query: | ||||
|             return auth_response_url_query['access_token'], auth_response_url_query['expires_in'] | ||||
|         else: | ||||
|             raise VkAuthError('OAuth2 authorization error') | ||||
|  | ||||
|     def login(self): | ||||
|         """ | ||||
|         Login | ||||
|         """ | ||||
|  | ||||
|         response = self.auth_session.get(self.LOGIN_URL) | ||||
|         login_form_action = get_form_action(response.text) | ||||
|         if not login_form_action: | ||||
|             raise VkAuthError('VK changed login flow') | ||||
|  | ||||
|         login_form_data = { | ||||
|             'email': self.user_login, | ||||
|             'pass': self.user_password, | ||||
|         } | ||||
|         response = self.auth_session.post(login_form_action, login_form_data) | ||||
|         logger.debug('Cookies: %s', self.auth_session.cookies) | ||||
|  | ||||
|         response_url_query = get_url_query(response.url) | ||||
|  | ||||
|         if 'remixsid' in self.auth_session.cookies or 'remixsid6' in self.auth_session.cookies: | ||||
|             return | ||||
|  | ||||
|         if 'sid' in response_url_query: | ||||
|             self.auth_captcha_is_needed(response, login_form_data) | ||||
|         elif response_url_query.get('act') == 'authcheck': | ||||
|             self.auth_check_is_needed(response.text) | ||||
|         elif 'security_check' in response_url_query: | ||||
|             self.phone_number_is_needed(response.text) | ||||
|         else: | ||||
|             message = 'Authorization error (incorrect password)' | ||||
|             logger.error(message) | ||||
|             raise VkAuthError(message) | ||||
|  | ||||
|     def oauth2_authorization(self): | ||||
|         """ | ||||
|         OAuth2 | ||||
|         """ | ||||
|         auth_data = { | ||||
|             'client_id': self.app_id, | ||||
|             'display': 'mobile', | ||||
|             'response_type': 'token', | ||||
|             'scope': self.scope, | ||||
|             'v': '5.28', | ||||
|         } | ||||
|         response = self.auth_session.post(self.AUTHORIZE_URL, auth_data) | ||||
|         response_url_query = get_url_query(response.url) | ||||
|         if 'access_token' in response_url_query: | ||||
|             return response_url_query | ||||
|  | ||||
|         # Permissions is needed | ||||
|         logger.info('Getting permissions') | ||||
|         # form_action = re.findall(r'<form method="post" action="(.+?)">', auth_response.text)[0] | ||||
|         form_action = get_form_action(response.text) | ||||
|         logger.debug('Response form action: %s', form_action) | ||||
|         if form_action: | ||||
|             response = self.auth_session.get(form_action) | ||||
|             response_url_query = get_url_query(response.url) | ||||
|             return response_url_query | ||||
|  | ||||
|         try: | ||||
|             response_json = response.json() | ||||
|         except ValueError:  # not JSON in response | ||||
|             error_message = 'OAuth2 grant access error' | ||||
|         else: | ||||
|             error_message = 'VK error: [{}] {}'.format(response_json['error'], response_json['error_description']) | ||||
|         logger.error('Permissions obtained') | ||||
|         raise VkAuthError(error_message) | ||||
|  | ||||
|     def auth_check_is_needed(self, html): | ||||
|         logger.info('User enabled 2 factors authorization. Auth check code is needed') | ||||
|         auth_check_form_action = get_form_action(html) | ||||
|         auth_check_code = self.get_auth_check_code() | ||||
|         auth_check_data = { | ||||
|             'code': auth_check_code, | ||||
|             '_ajax': '1', | ||||
|             'remember': '1' | ||||
|         } | ||||
|         response = self.auth_session.post(auth_check_form_action, data=auth_check_data) | ||||
|  | ||||
|     def auth_captcha_is_needed(self, response, login_form_data): | ||||
|         logger.info('Captcha is needed') | ||||
|  | ||||
|         response_url_dict = get_url_query(response.url) | ||||
|  | ||||
|         # form_url = re.findall(r'<form method="post" action="(.+)" novalidate>', response.text) | ||||
|         captcha_form_action = get_form_action(response.text) | ||||
|         logger.debug('form_url %s', captcha_form_action) | ||||
|         if not captcha_form_action: | ||||
|             raise VkAuthError('Cannot find form url') | ||||
|  | ||||
|         captcha_url = '%s?s=%s&sid=%s' % (self.CAPTCHA_URI, response_url_dict['s'], response_url_dict['sid']) | ||||
|         # logger.debug('Captcha url %s', captcha_url) | ||||
|  | ||||
|         login_form_data['captcha_sid'] = response_url_dict['sid'] | ||||
|         login_form_data['captcha_key'] = self.on_captcha_is_needed(captcha_url) | ||||
|  | ||||
|         response = self.auth_session.post(captcha_form_action, login_form_data) | ||||
|  | ||||
|         # logger.debug('Cookies %s', self.auth_session.cookies) | ||||
|         # if 'remixsid' not in self.auth_session.cookies and 'remixsid6' not in self.auth_session.cookies: | ||||
|         #     raise VkAuthError('Authorization error (Bad password or captcha key)') | ||||
|  | ||||
|     def phone_number_is_needed(self, text): | ||||
|         raise VkAuthError('Phone number is needed') | ||||
|  | ||||
|     def get_auth_check_code(self): | ||||
|         raise VkAuthError('Auth check code is needed') | ||||
|  | ||||
|  | ||||
| class InteractiveMixin(object): | ||||
|     def get_user_login(self): | ||||
|         user_login = raw_input('VK user login: ') | ||||
|         return user_login.strip() | ||||
|  | ||||
|     def get_user_password(self): | ||||
|         import getpass | ||||
|         user_password = getpass.getpass('VK user password: ') | ||||
|         return user_password | ||||
|  | ||||
|     def get_access_token(self): | ||||
|         logger.debug('InteractiveMixin.get_access_token()') | ||||
|         access_token, access_token_expires_in = super(InteractiveMixin, self).get_access_token() | ||||
|         if not access_token: | ||||
|             access_token = raw_input('VK API access token: ') | ||||
|             access_token_expires_in = None | ||||
|         return access_token, access_token_expires_in | ||||
|  | ||||
|     def on_captcha_is_needed(self, url): | ||||
|         """ | ||||
|         Read CAPTCHA key from shell | ||||
|         """ | ||||
|         print('Open captcha url:', url) | ||||
|         captcha_key = raw_input('Enter captcha key: ') | ||||
|         return captcha_key | ||||
|  | ||||
|     def get_auth_check_code(self): | ||||
|         """ | ||||
|         Read Auth code from shell | ||||
|         """ | ||||
|         auth_check_code = raw_input('Auth check code: ') | ||||
|         return auth_check_code.strip() | ||||
							
								
								
									
										48
									
								
								src/vk/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/vk/tests.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| # coding=utf8 | ||||
|  | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
|  | ||||
| import unittest | ||||
|  | ||||
| import vk | ||||
|  | ||||
| sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | ||||
|  | ||||
| # copy to test_props.py and fill it | ||||
| USER_LOGIN = ''         # user email or phone number | ||||
| USER_PASSWORD = ''      # user password | ||||
| APP_ID = ''             # aka API/Client ID | ||||
|  | ||||
| from test_props import USER_LOGIN, USER_PASSWORD, APP_ID | ||||
|  | ||||
|  | ||||
| class VkTestCase(unittest.TestCase): | ||||
|  | ||||
|     def setUp(self): | ||||
|         auth_session = vk.AuthSession(app_id=APP_ID, user_login=USER_LOGIN, user_password=USER_PASSWORD) | ||||
|         access_token, _ = auth_session.get_access_token() | ||||
|  | ||||
|         session = vk.Session(access_token=access_token) | ||||
|         self.vk_api = vk.API(session, lang='ru') | ||||
|  | ||||
|     def test_get_server_time(self): | ||||
|         time_1 = time.time() - 1 | ||||
|         time_2 = time_1 + 10 | ||||
|         server_time = self.vk_api.getServerTime() | ||||
|         self.assertTrue(time_1 <= server_time <= time_2) | ||||
|  | ||||
|     def test_get_server_time_via_token_api(self): | ||||
|         time_1 = time.time() - 1 | ||||
|         time_2 = time_1 + 10 | ||||
|         server_time = self.vk_api.getServerTime() | ||||
|         self.assertTrue(time_1 <= server_time <= time_2) | ||||
|  | ||||
|     def test_get_profiles_via_token(self): | ||||
|         profiles = self.vk_api.users.get(user_id=1) | ||||
|         self.assertEqual(profiles[0]['last_name'], u'Дуров') | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
							
								
								
									
										157
									
								
								src/vk/upload.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/vk/upload.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| @author: Kirill Python | ||||
| @contact: https://vk.com/python273 | ||||
| @license Apache License, Version 2.0, see LICENSE file | ||||
|  | ||||
| Copyright (C) 2015 | ||||
| """ | ||||
|  | ||||
|  | ||||
| class VkUpload(object): | ||||
|     def __init__(self, vk): | ||||
|         """ | ||||
|  | ||||
|         :param vk: объект VkApi | ||||
|         """ | ||||
|  | ||||
|         self.vk = vk | ||||
|         # https://vk.com/dev/upload_files | ||||
|  | ||||
|     def photo(self, photos, album_id, | ||||
|               latitude=None, longitude=None, caption=None, description=None, | ||||
|               group_id=None): | ||||
|         """ Загрузка изображений в альбом пользователя | ||||
|  | ||||
|         :param photos: список путей к изображениям, либо путь к изображению | ||||
|         :param album_id: идентификатор альбома | ||||
|         :param latitude: географическая широта, заданная в градусах | ||||
|                             (от -90 до 90) | ||||
|         :param longitude: географическая долгота, заданная в градусах | ||||
|                             (от -180 до 180) | ||||
|         :param caption: текст описания изображения | ||||
|         :param description: текст описания альбома | ||||
|         :param group_id: идентификатор сообщества (если загрузка идет в группу) | ||||
|         """ | ||||
|  | ||||
|         values = {'album_id': album_id} | ||||
|  | ||||
|         if group_id: | ||||
|             values['group_id'] = group_id | ||||
|  | ||||
|         # Получаем ссылку для загрузки | ||||
|         url = self.vk.photos.getUploadServer(values)['upload_url'] | ||||
|  | ||||
|         # Загружаем | ||||
|         photos_files = open_photos(photos) | ||||
|         response = self.vk.requests_session.post(url, files=photos_files).json() | ||||
|         close_photos(photos_files) | ||||
|  | ||||
|         # Олег Илларионов: | ||||
|         # это не могу к сожалению просто пофиксить | ||||
|         if 'album_id' not in response: | ||||
|             response['album_id'] = response['aid'] | ||||
|  | ||||
|         response.update({ | ||||
|             'latitude': latitude, | ||||
|             'longitude': longitude, | ||||
|             'caption': caption, | ||||
|             'description': description | ||||
|         }) | ||||
|  | ||||
|         values.update(response) | ||||
|  | ||||
|         # Сохраняем фото в альбоме | ||||
|         response = self.vk.photos.save(values) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def photo_messages(self, photos): | ||||
|         """ Загрузка изображений в сообщения | ||||
|  | ||||
|         :param photos: список путей к изображениям, либо путь к изображению | ||||
|         """ | ||||
|  | ||||
|         url = self.vk.method('photos.getMessagesUploadServer') | ||||
|         url = url['upload_url'] | ||||
|  | ||||
|         photos_files = open_photos(photos) | ||||
|         response = self.vk.http.post(url, files=photos_files) | ||||
|         close_photos(photos_files) | ||||
|  | ||||
|         response = self.vk.method('photos.saveMessagesPhoto', response.json()) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def photo_wall(self, photos, user_id=None, group_id=None): | ||||
|         """ Загрузка изображений на стену пользователя или в группу | ||||
|  | ||||
|         :param photos: список путей к изображениям, либо путь к изображению | ||||
|         :param user_id: идентификатор пользователя | ||||
|         :param group_id: идентификатор сообщества (если загрузка идет в группу) | ||||
|         """ | ||||
|  | ||||
|         values = {} | ||||
|  | ||||
|         if user_id: | ||||
|             values['user_id'] = user_id | ||||
|         elif group_id: | ||||
|             values['group_id'] = group_id | ||||
|         response = self.vk.photos.getWallUploadServer(**values) | ||||
|  | ||||
|         url = response['upload_url'] | ||||
|         print url | ||||
|         photos_files = open_photos(photos) | ||||
|         response = self.vk._session.requests_session.post(url, files=photos_files) | ||||
|         close_photos(photos_files) | ||||
|  | ||||
|         values.update(response.json()) | ||||
|         print values | ||||
|         response = self.vk.photos.saveWallPhoto(**values) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def document(self, file_path, title=None, tags=None, group_id=None): | ||||
|         """ Загрузка документа | ||||
|  | ||||
|         :param file_path: путь к документу | ||||
|         :param title: название документа | ||||
|         :param tags: метки для поиска | ||||
|         :param group_id: идентификатор сообщества (если загрузка идет в группу) | ||||
|         """ | ||||
|  | ||||
|         values = {'group_id': group_id} | ||||
|         url = self.vk.method('docs.getUploadServer', values)['upload_url'] | ||||
|  | ||||
|         with open(file_path, 'rb') as file: | ||||
|             response = self.vk.http.post(url, files={'file': file}).json() | ||||
|  | ||||
|         response.update({ | ||||
|             'title': title, | ||||
|             'tags': tags | ||||
|         }) | ||||
|  | ||||
|         response = self.vk.method('docs.save', response) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|  | ||||
| def open_photos(photos_paths): | ||||
|     if not isinstance(photos_paths, list): | ||||
|         photos_paths = [photos_paths] | ||||
|  | ||||
|     photos = [] | ||||
|  | ||||
|     for x, filename in enumerate(photos_paths): | ||||
|         filetype = filename.split('.')[-1] | ||||
|         photos.append( | ||||
|             ('file%s' % x, ('pic.' + filetype, open(filename, 'rb'))) | ||||
|         ) | ||||
|     print photos | ||||
|     return photos | ||||
|  | ||||
|  | ||||
| def close_photos(photos): | ||||
|     for photo in photos: | ||||
|         photo[1][1].close() | ||||
							
								
								
									
										73
									
								
								src/vk/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/vk/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
|  | ||||
| import logging | ||||
| from collections import Iterable | ||||
| import re | ||||
|  | ||||
| import requests | ||||
|  | ||||
|  | ||||
| STRING_TYPES = (str, bytes, bytearray) | ||||
|  | ||||
| logger = logging.getLogger('vk') | ||||
|  | ||||
|  | ||||
| try: | ||||
|     # Python 2 | ||||
|     from urllib import urlencode | ||||
|     from urlparse import urlparse, parse_qsl | ||||
| except ImportError: | ||||
|     # Python 3 | ||||
|     from urllib.parse import urlparse, parse_qsl, urlencode | ||||
|  | ||||
|  | ||||
| try: | ||||
|     import simplejson as json | ||||
| except ImportError: | ||||
|     import json | ||||
|  | ||||
|  | ||||
| try: | ||||
|     # Python 2 | ||||
|     raw_input = raw_input | ||||
| except NameError: | ||||
|     # Python 3 | ||||
|     raw_input = input | ||||
|  | ||||
|  | ||||
| def json_iter_parse(response_text): | ||||
|     decoder = json.JSONDecoder(strict=False) | ||||
|     idx = 0 | ||||
|     while idx < len(response_text): | ||||
|         obj, idx = decoder.raw_decode(response_text, idx) | ||||
|         yield obj | ||||
|  | ||||
|  | ||||
| def stringify_values(dictionary): | ||||
|     stringified_values_dict = {} | ||||
|     for key, value in dictionary.items(): | ||||
|         if isinstance(value, Iterable) and not isinstance(value, STRING_TYPES): | ||||
|             value = ','.join(map(str, value)) | ||||
|         stringified_values_dict[key] = value | ||||
|     return stringified_values_dict | ||||
|  | ||||
|  | ||||
| def get_url_query(url): | ||||
|     parsed_url = urlparse(url) | ||||
|     url_query = parse_qsl(parsed_url.fragment) | ||||
|     # login_response_url_query can have multiple key | ||||
|     url_query = dict(url_query) | ||||
|     return url_query | ||||
|  | ||||
|  | ||||
| def get_form_action(html): | ||||
|     form_action = re.findall(r'<form(?= ).* action="(.+)"', html) | ||||
|     if form_action: | ||||
|         return form_action[0] | ||||
|  | ||||
|  | ||||
| class LoggingSession(requests.Session): | ||||
|     def request(self, method, url, **kwargs): | ||||
|         logger.debug('Request: %s %s, params=%r, data=%r', method, url, kwargs.get('params'), kwargs.get('data')) | ||||
|         response = super(LoggingSession, self).request(method, url, **kwargs) | ||||
|         logger.debug('Response: %s %s', response.status_code, response.url) | ||||
|         return response | ||||
							
								
								
									
										5
									
								
								src/widgetUtils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/widgetUtils/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| import platform | ||||
| if platform.system() == "Windows": | ||||
|  from wxUtils import * | ||||
| #elif platform.system() == "Linux": | ||||
| # from gtkUtils import * | ||||
							
								
								
									
										143
									
								
								src/widgetUtils/gtkUtils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/widgetUtils/gtkUtils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| from gi.repository import Gtk, Gdk | ||||
| from gi.repository import GObject | ||||
|  | ||||
| toolkit = "gtk" | ||||
| # Code responses for GTK +3 dialogs. | ||||
| # this is when an user presses OK on a dialogue. | ||||
| OK = Gtk.ResponseType.OK | ||||
| # This is when an user presses cancel on a dialogue. | ||||
| CANCEL = Gtk.ResponseType.CANCEL | ||||
| # This is when an user closes the dialogue or an id to create the close button. | ||||
| CLOSE = Gtk.ResponseType.CLOSE | ||||
| # The response for a "yes" Button pressed on a dialogue. | ||||
| YES = Gtk.ResponseType.YES | ||||
| # This is when the user presses No on a default dialogue. | ||||
| NO = Gtk.ResponseType.NO | ||||
|  | ||||
| #events | ||||
| # This is raised when the application must be closed. | ||||
| CLOSE_EVENT = "delete-event" | ||||
| # This is activated when a button  is pressed. | ||||
| BUTTON_PRESSED = "clicked" | ||||
| # This is activated when an user enter text on an edit box. | ||||
| ENTERED_TEXT = "changed" | ||||
| MENU = "activate" | ||||
|  | ||||
| #KEYPRESS = wx.EVT_CHAR_HOOK | ||||
| #NOTEBOOK_PAGE_CHANGED = wx.EVT_NOTEBOOK_PAGE_CHANGED | ||||
| CHECKBOX = "toggled" | ||||
|  | ||||
| def exit_application(): | ||||
|  """ Closes the current window cleanly. """ | ||||
|  Gtk.main_quit() | ||||
|  | ||||
| def connect_event(parent, event, func, menuitem=None, *args, **kwargs): | ||||
|  """ Connects an event to a function. | ||||
|   parent Gtk.widget: The widget that will listen for the event. | ||||
|   event widgetUtils.event: The event that will be listened for the parent. The event should be one of the widgetUtils events. | ||||
|   function func: The function that will be connected to the event.""" | ||||
|  if menuitem == None: | ||||
|   return getattr(parent, "connect")(event, func, *args, **kwargs) | ||||
|  else: | ||||
|   return getattr(menuitem, "connect")(event, func, *args, **kwargs) | ||||
|  | ||||
| class list(object): | ||||
|  def __init__(self, *columns, **listArguments): | ||||
|   self.columns = columns | ||||
|   self.list_arguments = listArguments | ||||
|   self.create_list() | ||||
|  | ||||
|  def create_list(self): | ||||
|   columns = [] | ||||
|   [columns.append(str) for i in self.columns] | ||||
|   self.store = Gtk.ListStore(*columns) | ||||
|   self.list = Gtk.TreeView(model=self.store) | ||||
|   renderer = Gtk.CellRendererText() | ||||
|   for i in range(0, len(self.columns)): | ||||
|    column = Gtk.TreeViewColumn(self.columns[i], renderer, text=i) | ||||
| #   column.set_sort_column_id(i)         | ||||
|    self.list.append_column(column) | ||||
|  | ||||
|  def insert_item(self, reversed=False, *item): | ||||
|   if reversed == False: | ||||
|    self.store.append(row=item) | ||||
|   else: | ||||
|    self.store.insert(position=0, row=item) | ||||
|  | ||||
|  def get_selected(self): | ||||
|   tree_selection = self.list.get_selection() | ||||
|   (model, pathlist) = tree_selection.get_selected_rows() | ||||
|   return int(pathlist[0].to_string() ) | ||||
|  | ||||
|  def select_item(self, item): | ||||
|   tree_selection = self.list.get_selection() | ||||
|   tree_selection.select_path(item) | ||||
|  | ||||
|  def remove_item(self, item): | ||||
|   self.store.remove(self.store.get_iter(item)) | ||||
|  | ||||
|  def get_count(self): | ||||
|   return len(self.store) | ||||
|  | ||||
| class baseDialog(Gtk.Dialog): | ||||
|  def __init__(self, *args, **kwargs): | ||||
|   super(baseDialog, self).__init__(*args, **kwargs) | ||||
|   self.box = self.get_content_area() | ||||
|  | ||||
|  def get_response(self): | ||||
|   answer = self.run() | ||||
|   return answer | ||||
|  | ||||
| class buffer(GObject.GObject): | ||||
|  name = GObject.property(type=str) | ||||
|  | ||||
|  def __init__(self, obj): | ||||
|   super(buffer, self).__init__() | ||||
|   self.buffer = obj | ||||
|  | ||||
| class notebook(object): | ||||
|  | ||||
|  def __init__(self): | ||||
|   self.store = Gtk.TreeStore(buffer.__gtype__) | ||||
|   self.view = Gtk.TreeView() | ||||
|   self.view.set_model(self.store) | ||||
|  | ||||
|   column = Gtk.TreeViewColumn("Buffer") | ||||
|   cell = Gtk.CellRendererText() | ||||
|   column.pack_start(cell, True) | ||||
|   column.set_cell_data_func(cell, self.get_buffer) | ||||
|   self.view.append_column(column) | ||||
|  | ||||
|  def get_current_page(self): | ||||
|   tree_selection = self.view.get_selection() | ||||
|   (model, pathlist) = tree_selection.get_selected_rows() | ||||
|   iter = pathlist[0] | ||||
|   return self.store[iter][0].buffer | ||||
|  | ||||
|  def get_buffer(self, column, cell, model, iter, data): | ||||
|   cell.set_property('text', self.store.get_value(iter, 0).name) | ||||
|  | ||||
|  def match_func(self, row, name_, account): | ||||
|   name = name_ | ||||
|   account = account | ||||
|   iter = self.store.get_iter(row.path) | ||||
|   if self.store[iter][0].buffer.name == name and self.store[iter][0].buffer.account == account: | ||||
|    return (row.path, iter) | ||||
|   else: | ||||
|    return (None, None) | ||||
|  | ||||
|  def search(self, rows, name_, account): | ||||
|   if not rows: return None | ||||
|   for row in rows: | ||||
|    (path, iter) = self.match_func(row, name_, account) | ||||
|    if iter != None: | ||||
|     return (path, iter) | ||||
|    (result_path, result_iter) = self.search(row.iterchildren(), name_, account) | ||||
|    if result_path: return (result_path, result_iter) | ||||
|   return (None, None) | ||||
|  | ||||
| class mainLoopObject(object): | ||||
|  | ||||
|  def run(self): | ||||
|   GObject.type_register(buffer) | ||||
|   Gtk.main() | ||||
							
								
								
									
										171
									
								
								src/widgetUtils/wxUtils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/widgetUtils/wxUtils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| import wx | ||||
| import paths | ||||
| import languageHandler | ||||
| import sys | ||||
|  | ||||
| toolkit = "wx" | ||||
|  | ||||
| ### Code responses for WX dialogs. | ||||
|  | ||||
| # this is when an user presses OK on a dialogue. | ||||
| OK = wx.ID_OK | ||||
|  | ||||
| # This is when an user presses cancel on a dialogue. | ||||
| CANCEL = wx.ID_CANCEL | ||||
|  | ||||
| # This is when an user closes the dialogue or an id to create the close button. | ||||
| CLOSE = wx.ID_CLOSE | ||||
|  | ||||
| # The response for a "yes" Button pressed on a dialogue. | ||||
| YES = wx.ID_YES | ||||
|  | ||||
| # This is when the user presses No on a default dialogue. | ||||
| NO = wx.ID_NO | ||||
|  | ||||
| ###events | ||||
|  | ||||
| # This is raised when the application must be closed. | ||||
| CLOSE_EVENT = wx.EVT_CLOSE | ||||
|  | ||||
| # This is activated when a button  is pressed. | ||||
| BUTTON_PRESSED = wx.EVT_BUTTON | ||||
|  | ||||
| # This is raised when a checkbox changes its status. | ||||
| CHECKBOX = wx.EVT_CHECKBOX | ||||
|  | ||||
| # This is activated when an user enter text on an edit box. | ||||
| ENTERED_TEXT = wx.EVT_TEXT | ||||
|  | ||||
| # This is raised when a user activates a menu. | ||||
| MENU = wx.EVT_MENU | ||||
|  | ||||
| # This is raised when a user presses any key in the control. | ||||
| KEYPRESS = wx.EVT_CHAR_HOOK | ||||
|  | ||||
| # This is raised when a user releases a key in the control. | ||||
| KEYUP = wx.EVT_KEY_UP | ||||
|  | ||||
| # This happens when a notebook tab is changed, It is used in Treebooks too. | ||||
| NOTEBOOK_PAGE_CHANGED = wx.EVT_TREEBOOK_PAGE_CHANGED | ||||
|  | ||||
| # This happens when a radiobutton group changes its status. | ||||
| RADIOBUTTON = wx.EVT_RADIOBUTTON | ||||
|  | ||||
| # Taskbar mouse clicks. | ||||
| TASKBAR_RIGHT_CLICK = wx.EVT_TASKBAR_RIGHT_DOWN | ||||
| TASKBAR_LEFT_CLICK = wx.EVT_TASKBAR_LEFT_DOWN | ||||
|  | ||||
| def exit_application(): | ||||
| 	""" Closes the current window cleanly. """ | ||||
| 	wx.GetApp().ExitMainLoop() | ||||
|  | ||||
| def connect_event(parent, event, func, menuitem=None, *args, **kwargs): | ||||
| 	""" Connects an event to a function. | ||||
| 	parent wx.window: The widget that will listen for the event. | ||||
| 	event widgetUtils.event: The event that will be listened for the parent. The event should be one of the widgetUtils events. | ||||
| 	function func: The function that will be connected to the event.""" | ||||
| 	if menuitem == None: | ||||
| 		return getattr(parent, "Bind")(event, func, *args, **kwargs) | ||||
| 	else: | ||||
| 		return getattr(parent, "Bind")(event, func, menuitem, *args, **kwargs) | ||||
|  | ||||
| def connectExitFunction(exitFunction): | ||||
| 	""" This connect the events in WX when an user is  turning off the machine.""" | ||||
| 	wx.GetApp().Bind(wx.EVT_QUERY_END_SESSION, exitFunction) | ||||
| 	wx.GetApp().Bind(wx.EVT_END_SESSION, exitFunction) | ||||
|  | ||||
| class BaseDialog(wx.Dialog): | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		super(BaseDialog, self).__init__(*args, **kwargs) | ||||
|  | ||||
| 	def get_response(self): | ||||
| 		return self.ShowModal() | ||||
|  | ||||
| 	def get(self, control): | ||||
| 		if hasattr(self, control): | ||||
| 			control = getattr(self, control) | ||||
| 			if hasattr(control, "GetValue"): return getattr(control, "GetValue")() | ||||
| 			elif hasattr(control, "GetLabel"): return getattr(control, "GetLabel")() | ||||
| 			else: return -1 | ||||
| 		else: return 0 | ||||
|  | ||||
| 	def set(self, control, text): | ||||
| 		if hasattr(self, control): | ||||
| 			control = getattr(self, control) | ||||
| 			if hasattr(control, "SetValue"): return getattr(control, "SetValue")(text) | ||||
| 			elif hasattr(control, "SetLabel"): return getattr(control, "SetLabel")(text) | ||||
| 			elif hasattr(control, "ChangeValue"): return getattr(control, "ChangeValue")(text) | ||||
| 			else: return -1 | ||||
| 		else: return 0 | ||||
|  | ||||
| 	def destroy(self): | ||||
| 		self.Destroy() | ||||
|  | ||||
| 	def set_title(self, title): | ||||
| 		self.SetTitle(title) | ||||
|  | ||||
| 	def get_title(self): | ||||
| 		return self.GetTitle() | ||||
|  | ||||
| class mainLoopObject(wx.App): | ||||
|  | ||||
| 	def __init__(self): | ||||
| 		self.app = wx.App() | ||||
| 		self.lc = wx.Locale() | ||||
| 		lang=languageHandler.getLanguage() | ||||
| 		wxLang=self.lc.FindLanguageInfo(lang) | ||||
| 		if not wxLang and '_' in lang: | ||||
| 			wxLang=self.lc.FindLanguageInfo(lang.split('_')[0]) | ||||
| 		if hasattr(sys,'frozen'): | ||||
| 			self.lc.AddCatalogLookupPathPrefix(paths.app_path("locales")) | ||||
| 		if wxLang: | ||||
| 			self.lc.Init(wxLang.Language) | ||||
|  | ||||
| 	def run(self): | ||||
| 		self.app.MainLoop() | ||||
|  | ||||
| class list(object): | ||||
|  def __init__(self, parent, *columns, **listArguments): | ||||
|   self.columns = columns | ||||
|   self.listArguments = listArguments | ||||
|   self.create_list(parent) | ||||
|  | ||||
|  def set_windows_size(self, column, characters_max): | ||||
|   self.list.SetColumnWidth(column, characters_max*2) | ||||
|  | ||||
|  def set_size(self): | ||||
|   self.list.SetSize((self.list.GetBestSize()[0], 728)) | ||||
|  | ||||
|  def create_list(self, parent): | ||||
|   self.list = wx.ListCtrl(parent, -1, **self.listArguments) | ||||
|   for i in xrange(0, len(self.columns)): | ||||
|    self.list.InsertColumn(i, u"%s" % (self.columns[i])) | ||||
|  | ||||
|  def insert_item(self, reversed, *item): | ||||
|   """ Inserts an item on the list.""" | ||||
|   if reversed == False: items = self.list.GetItemCount() | ||||
|   else: items = 0 | ||||
|   self.list.InsertStringItem(items, item[0]) | ||||
|   for i in xrange(1, len(self.columns)): | ||||
|    self.list.SetStringItem(items, i, item[i]) | ||||
|  | ||||
|  def remove_item(self, pos): | ||||
|   """ Deletes an item from the list.""" | ||||
|   if pos > 0: self.list.Focus(pos-1) | ||||
|   self.list.DeleteItem(pos) | ||||
|  | ||||
|  def clear(self): | ||||
|   self.list.DeleteAllItems() | ||||
|  | ||||
|  def get_selected(self): | ||||
|   return self.list.GetFocusedItem() | ||||
|  | ||||
|  def select_item(self, pos): | ||||
|   self.list.Focus(pos) | ||||
|  | ||||
|  def get_count(self): | ||||
|   selected = self.list.GetItemCount() | ||||
|   if selected == -1: | ||||
|    return 0 | ||||
|   else: | ||||
|    return selected | ||||
							
								
								
									
										0
									
								
								src/wxUI/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/wxUI/__init__.py
									
									
									
									
									
										Normal file
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user