Port socializer to Python 3. #16

This commit is contained in:
2019-01-02 04:42:53 +03:00
parent 1aeab0aef5
commit 4442931dd4
68 changed files with 967 additions and 1006 deletions

View File

@@ -1,6 +1,6 @@
import os
import ssl
import utils
from . import utils
try:
context = ssl.create_default_context()

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from UserDict import UserDict
from collections import UserDict
from configobj import ConfigObj, ParseError
from validate import Validator, VdtValueError
import os

View File

@@ -1,9 +1,11 @@
import _sslfixer
from builtins import range
import webbrowser
import random
import requests
import string
from wxUI import two_factor_auth
from . import _sslfixer
from .wxUI import two_factor_auth
class AuthenticationError(Exception):
pass

View File

@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
""" this module contains everything used to render different kind of posts (posts in the home buffer,
Chat messages, audios, videos, photos, comments in posts, etc)"""
from __future__ import unicode_literals
from builtins import range
import arrow
import languageHandler
import logging
import utils
from . utils import seconds_to_string
log = logging.getLogger(__file__)
@@ -15,29 +17,29 @@ def extract_attachment(attachment):
This will produce a result like:
'website: http://url.com'.
'photo: A forest'."""
msg = u""
msg = ""
if attachment["type"] == "link":
msg = u"{0}: {1}".format(attachment["link"]["title"], attachment["link"]["url"])
msg = "{0}: {1}".format(attachment["link"]["title"], attachment["link"]["url"])
elif attachment["type"] == "photo":
msg = attachment["photo"]["text"]
if msg == "":
return _(u"photo with no description available")
return _("photo with no description available")
elif attachment["type"] == "video":
msg = _(u"video: {0}").format(attachment["video"]["title"],)
msg = _("video: {0}").format(attachment["video"]["title"],)
return msg
def short_text(status):
""" This shorts the text to 140 characters for displaying it in the list control of buffers."""
message = ""
# copy_story indicates that the post is a shared repost.
if status.has_key("copy_history"):
if "copy_history" in status:
txt = status["copy_history"][0]["text"]
else:
txt = status["text"]
if len(txt) < 140:
message = utils.clean_text(txt)
message = clean_text(txt)
else:
message = utils.clean_text(txt[:139])
message = clean_text(txt[:139])
return message
def clean_audio(audio):
@@ -48,20 +50,53 @@ def clean_audio(audio):
audio["count"] = audio["count"] -1
return audio
def clean_text(text):
""" Replaces all HTML entities and put the plain text equivalent if it's possible."""
text = text.replace("<br>", "\n")
text = text.replace("\\n", "\n")
return text
def add_attachment(attachment):
msg = ""
tpe = ""
if attachment["type"] == "link":
msg = "{0}: {1}".format(attachment["link"]["title"], attachment["link"]["url"])
tpe = _("Link")
elif attachment["type"] == "photo":
tpe = _("Photo")
msg = attachment["photo"]["text"]
if msg == "":
msg = _("no description available")
elif attachment["type"] == "video":
msg = "{0}".format(attachment["video"]["title"],)
tpe = _("Video")
elif attachment["type"] == "audio":
msg = "{0}".format(" ".join(render_audio(attachment["audio"])))
tpe = _("Audio")
elif attachment["type"] == "doc":
msg = "{0}".format(attachment["doc"]["title"])
tpe = _("{0} file").format(attachment["doc"]["ext"])
elif attachment["type"] == "audio_message":
msg = "{0}".format(" ".join(render_audio_message(attachment["audio_message"])))
tpe = _("Voice message")
else:
print(attachment)
return [tpe, msg]
### Render functions
def render_person(status, session):
""" Render users in people buffers such as everything related to friendships or buffers created with only people.
Example result: ["John Doe", "An hour ago"]
Reference: https://vk.com/dev/fields"""
if status.has_key("last_seen"):
if "last_seen" in status:
original_date = arrow.get(status["last_seen"]["time"])
# Translators: This is the date of last seen
last_seen = _(u"{0}").format(original_date.humanize(locale=languageHandler.curLang[:2]),)
last_seen = _("{0}").format(original_date.humanize(locale=languageHandler.curLang[:2]),)
# Account suspended or deleted.
elif status.has_key("last_seen") == False and status.has_key("deactivated"):
last_seen = _(u"Account deactivated")
return [u"{0} {1}".format(status["first_name"], status["last_name"]), last_seen]
elif ("last_seen" in status) == False and "deactivated" in status:
last_seen = _("Account deactivated")
return ["{0} {1}".format(status["first_name"], status["last_name"]), last_seen]
def render_newsfeed_item(status, session):
""" This me☻thod is used to render an item of the news feed.
@@ -72,15 +107,15 @@ def render_newsfeed_item(status, session):
"""
user = session.get_user_name(status["source_id"], case_name="nom")
# See if this is a post or repost.
if status.has_key("copy_history"):
user = _(u"{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_history"][0]["owner_id"]))
if "copy_history" in status:
user = _("{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_history"][0]["owner_id"]))
message = ""
original_date = arrow.get(status["date"])
created_at = original_date.humanize(locale=languageHandler.curLang[:2])
# handle status updates.
if status["type"] == "post":
message += short_text(status)
if status.has_key("attachment") and len(status["attachment"]) > 0:
if "attachment" in status and len(status["attachment"]) > 0:
message += extract_attachment(status["attachment"])
# If there is no message after adding text, it's because a pphoto with no description has been found.
# so let's manually add the "no description" tag here.
@@ -91,43 +126,43 @@ def render_newsfeed_item(status, session):
# removes deleted audios.
status["audio"] = clean_audio(status["audio"])
if status["audio"]["count"] == 1:
message = _(u"{0} has added an audio: {1}").format(user, u", ".join(render_audio(status["audio"]["items"][0], session)),)
message = _("{0} has added an audio: {1}").format(user, ", ".join(render_audio(status["audio"]["items"][0], session)),)
else:
prem = ""
for i in xrange(0, status["audio"]["count"]):
for i in range(0, status["audio"]["count"]):
composed_audio = render_audio(status["audio"]["items"][i], session)
prem += u"{0} - {1}, ".format(composed_audio[0], composed_audio[1])
message = _(u"{0} has added {1} audios: {2}").format(user, status["audio"]["count"], prem)
prem += "{0} - {1}, ".format(composed_audio[0], composed_audio[1])
message = _("{0} has added {1} audios: {2}").format(user, status["audio"]["count"], prem)
# Handle audio playlists
elif status["type"] == "audio_playlist":
if status["audio_playlist"]["count"] == 1:
message = _(u"{0} has added an audio album: {1}, {2}").format(user, status["audio_playlist"]["items"][0]["title"], status["audio_playlist"]["items"][0]["description"])
message = _("{0} has added an audio album: {1}, {2}").format(user, status["audio_playlist"]["items"][0]["title"], status["audio_playlist"]["items"][0]["description"])
else:
prestring = ""
for i in xrange(0, status["audio_playlist"]["count"]):
prestring += u"{0} - {1}, ".format(status["audio_playlist"]["items"][i]["title"], status["audio_playlist"]["items"][i]["description"])
message = _(u"{0} has added {1} audio albums: {2}").format(user, status["audio_playlist"]["count"], prestring)
for i in range(0, status["audio_playlist"]["count"]):
prestring += "{0} - {1}, ".format(status["audio_playlist"]["items"][i]["title"], status["audio_playlist"]["items"][i]["description"])
message = _("{0} has added {1} audio albums: {2}").format(user, status["audio_playlist"]["count"], prestring)
# handle new friends for people in the news buffer.
elif status["type"] == "friend":
msg_users = u""
if status.has_key("friends"):
msg_users = ""
if "friends" in status:
for i in status["friends"]["items"]:
msg_users = msg_users + u"{0}, ".format(session.get_user_name(i["user_id"], "nom"))
msg_users = msg_users + "{0}, ".format(session.get_user_name(i["user_id"], "nom"))
else:
print status.keys()
message = _(u"{0} added friends: {1}").format(user, msg_users)
print(list(status.keys()))
message = _("{0} added friends: {1}").format(user, msg_users)
elif status["type"] == "video":
if status["video"]["count"] == 1:
message = _(u"{0} has added a video: {1}").format(user, u", ".join(render_video(status["video"]["items"][0], session)),)
message = _("{0} has added a video: {1}").format(user, ", ".join(render_video(status["video"]["items"][0], session)),)
else:
prem = ""
for i in xrange(0, status["video"]["count"]):
for i in range(0, status["video"]["count"]):
composed_video = render_video(status["video"]["items"][i], session)
prem += u"{0} - {1}, ".format(composed_video[0], composed_video[1])
message = _(u"{0} has added {1} videos: {2}").format(user, status["video"]["count"], prem)
prem += "{0} - {1}, ".format(composed_video[0], composed_video[1])
message = _("{0} has added {1} videos: {2}").format(user, status["video"]["count"], prem)
else:
if status["type"] != "post": print status
if status["type"] != "post": print(status)
return [user, message, created_at]
def render_message(message, session):
@@ -139,30 +174,30 @@ def render_message(message, session):
original_date = original_date.to(now.tzinfo)
# Format the date here differently depending in if this is the same day for both dates or not.
if original_date.day == now.day:
created_at = original_date.format(_(u"H:mm."), locale=languageHandler.curLang[:2])
created_at = original_date.format(_("H:mm."), locale=languageHandler.curLang[:2])
else:
created_at = original_date.format(_(u"H:mm. dddd, MMMM D, YYYY"), locale=languageHandler.curLang[:2])
created_at = original_date.format(_("H:mm. dddd, MMMM D, YYYY"), locale=languageHandler.curLang[:2])
# No idea why some messages send "text" instead "body"
if message.has_key("body"):
if "body" in message:
body = message["body"]
else:
body = message["text"]
return [u"{2}, {0} {1}".format(body, created_at, user)]
return ["{2}, {0} {1}".format(body, created_at, user)]
def render_status(status, session):
""" Render a wall post (shown in user's wall, not in newsfeed).
Reference: https://vk.com/dev/post"""
user = session.get_user_name(status["from_id"], "nom")
if status.has_key("copy_history"):
user = _(u"{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_history"][0]["owner_id"]))
if "copy_history" in status:
user = _("{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_history"][0]["owner_id"]))
message = ""
original_date = arrow.get(status["date"])
created_at = original_date.humanize(locale=languageHandler.curLang[:2])
if status.has_key("copy_owner_id"):
user = _(u"{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_owner_id"]))
if "copy_owner_id" in status:
user = _("{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_owner_id"]))
if status["post_type"] == "post" or status["post_type"] == "copy":
message += short_text(status)
if status.has_key("attachment") and len(status["attachment"]) > 0:
if "attachment" in status and len(status["attachment"]) > 0:
message += extract_attachment(status["attachment"])
if message == "":
message = "no description available"
@@ -173,8 +208,8 @@ def render_audio(audio, session=None):
Example result:
["Song title", "Artist", "03:15"]
reference: https://vk.com/dev/audio_object"""
if audio == False: return [_(u"Audio removed from library"), "", ""]
return [audio["title"], audio["artist"], utils.seconds_to_string(audio["duration"])]
if audio == False: return [_("Audio removed from library"), "", ""]
return [audio["title"], audio["artist"], seconds_to_string(audio["duration"])]
def render_video(video, session=None):
""" Render a video file from VK.
@@ -182,13 +217,13 @@ def render_video(video, session=None):
["Video title", "Video description", "01:30:28"]
Reference: https://vk.com/dev/video_object"""
if video == False:
return [_(u"Video not available"), "", ""]
return [video["title"], video["description"], utils.seconds_to_string(video["duration"])]
return [_("Video not available"), "", ""]
return [video["title"], video["description"], seconds_to_string(video["duration"])]
def render_audio_message(audio_message, session=None):
""" Render a voice message from VK
Example result:
["Voice message", "01:30:28"]"""
if audio_message == False:
return [_(u"Voice message not available"), "", ""]
return [utils.seconds_to_string(audio_message["duration"])]
return [_("Voice message not available"), "", ""]
return [seconds_to_string(audio_message["duration"])]

View File

@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
""" Session object for Socializer. A session is the only object to call VK API methods, save settings and access to the cache database and sound playback mechanisms. """
from __future__ import unicode_literals
import os
import logging
import languageHandler
import paths
import vkSessionHandler
from . import vkSessionHandler
import sound
from config_utils import Configuration, ConfigurationResetException
from .config_utils import Configuration, ConfigurationResetException
from pubsub import pub
from vk_api.exceptions import LoginRequired, VkApiError
@@ -31,14 +32,14 @@ def find_item(list, item):
global identifiers
identifier = None
for i in identifiers:
if item.has_key(i):
if i in item:
identifier = i
break
if identifier == None:
# if there are objects that can't be processed by lack of identifier, let's print keys for finding one.
log.exception("Can't find an identifier for the following object: %r" % (item.keys(),))
log.exception("Can't find an identifier for the following object: %r" % (list(item.keys()),))
for i in list:
if i.has_key(identifier) and i[identifier] == item[identifier]:
if identifier in i and i[identifier] == item[identifier]:
return True
return False
@@ -53,12 +54,12 @@ class vkSession(object):
global post_types
first_addition = False
num = 0
if self.db.has_key(name) == False:
if (name in self.db) == False:
self.db[name] = {}
self.db[name]["items"] = []
first_addition = True
for i in data:
if i.has_key("type") and (i["type"] == "wall_photo" or i["type"] == "photo_tag" or i["type"] == "photo"):
if "type" in i and (i["type"] == "wall_photo" or i["type"] == "photo_tag" or i["type"] == "photo"):
log.debug("Skipping unsupported item... %r" % (i,))
continue
# for some reason, VK sends post data if the post has been deleted already.
@@ -127,7 +128,7 @@ class vkSession(object):
def get_newsfeed(self, name="newsfeed", show_nextpage=False, endpoint="", *args, **kwargs):
log.debug("Updating news feed...")
if show_nextpage == True and self.db[name].has_key("cursor"):
if show_nextpage == True and "cursor" in self.db[name]:
log.debug("user has requested previous items")
kwargs["start_from"] = self.db[name]["cursor"]
log.debug("Params for sending to vk: %r" % (kwargs,))
@@ -138,8 +139,8 @@ class vkSession(object):
# else:
# print data.keys(), len(data["items"]), data["next_from"]
num = self.order_buffer(name, data["items"], show_nextpage)
log.debug("Keys of the returned data for debug purposes: %r" % (data.keys(),))
if data.has_key("next_from"):
log.debug("Keys of the returned data for debug purposes: %r" % (list(data.keys()),))
if "next_from" in data:
self.db[name]["cursor"] = data["next_from"]
return num
@@ -150,7 +151,7 @@ class vkSession(object):
c = self.vk.client_audio
else:
c = self.vk.client
if kwargs.has_key("parent_endpoint"):
if "parent_endpoint" in kwargs:
p = kwargs["parent_endpoint"]
if "audio" in p and self.settings["vk"]["use_alternative_tokens"]:
log.info("Using alternative audio methods.")
@@ -165,12 +166,12 @@ class vkSession(object):
if data != None:
if type(data) == dict:
num = self.order_buffer(name, data["items"], show_nextpage)
if len(data["items"]) > 0 and data["items"][0].has_key("first_name"):
if len(data["items"]) > 0 and "first_name" in data["items"][0]:
data2 = {"profiles": [], "groups": []}
for i in data["items"]:
data2["profiles"].append(i)
self.process_usernames(data2)
if data.has_key("profiles") and data.has_key("groups"):
if "profiles" in data and "groups" in data:
self.process_usernames(data)
else:
num = self.order_buffer(name, data, show_nextpage)
@@ -185,15 +186,15 @@ class vkSession(object):
def get_user_name(self, user_id, case_name="gen"):
if user_id > 0:
if self.db["users"].has_key(user_id):
if self.db["users"][user_id].has_key(case_name):
if user_id in self.db["users"]:
if case_name in self.db["users"][user_id]:
return self.db["users"][user_id][case_name]
else:
return self.db["users"][user_id]["nom"]
else:
return "no specified user"
else:
if self.db["groups"].has_key(abs(user_id)):
if abs(user_id) in self.db["groups"]:
return self.db["groups"][abs(user_id)]["nom"]
else:
return "no specified community"
@@ -203,7 +204,7 @@ class vkSession(object):
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["id"]] = u"{0} {1}".format(i["first_name"], i["last_name"])
self.db["users"][i["id"]] = "{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:
@@ -217,8 +218,8 @@ class vkSession(object):
log.debug("Adding usernames to the local database...")
ids = ""
for i in data["profiles"]:
if self.db["users"].has_key(i["id"]) == False:
self.db["users"][i["id"]] = dict(nom=u"{0} {1}".format(i["first_name"], i["last_name"]))
if (i["id"] in self.db["users"]) == False:
self.db["users"][i["id"]] = dict(nom="{0} {1}".format(i["first_name"], i["last_name"]))
ids = ids + "{0},".format(i["id"],)
gids = ""
for i in data["groups"]:
@@ -230,11 +231,11 @@ class vkSession(object):
users_genitive = self.vk.client.users.get(user_ids=ids, fields="first_name, last_name", name_case="gen")
users_instrumental = self.vk.client.users.get(user_ids=ids, fields="first_name, last_name", name_case="ins")
for i in users_genitive:
if self.db["users"].has_key(i["id"]):
self.db["users"][i["id"]]["gen"] = u"{0} {1}".format(i["first_name"], i["last_name"])
if i["id"] in self.db["users"]:
self.db["users"][i["id"]]["gen"] = "{0} {1}".format(i["first_name"], i["last_name"])
for i in users_instrumental:
if self.db["users"].has_key(i["id"]):
self.db["users"][i["id"]]["ins"] = u"{0} {1}".format(i["first_name"], i["last_name"])
if i["id"] in self.db["users"]:
self.db["users"][i["id"]]["ins"] = "{0} {1}".format(i["first_name"], i["last_name"])
def get_my_data(self):
log.debug("Getting user identifier...")

View File

@@ -2,12 +2,12 @@
import os
import sys
import widgetUtils
import wxUI as view
from . import wxUI as view
import paths
import time
import logging
import session
from config_utils import Configuration
from . import session
from .config_utils import Configuration
log = logging.getLogger("sessionmanager.sessionManager")

View File

@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
""" Some utilities. I no have idea how I should put these, so..."""
from __future__ import division
from __future__ import unicode_literals
import os
import requests
import re
import logging
from sessionmanager import renderers
log = logging.getLogger("utils")
url_re = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ ]*")
bad_chars = '\'\\.,[](){}:;"'
@@ -18,21 +20,21 @@ def seconds_to_string(seconds, precision=0):
sec_string = sec.__format__(sec_spec)
string = ""
if day == 1:
string += _(u"%d day, ") % day
string += _("%d day, ") % day
elif day >= 2:
string += _(u"%d days, ") % day
string += _("%d days, ") % day
if (hour == 1):
string += _(u"%d hour, ") % hour
string += _("%d hour, ") % hour
elif (hour >= 2):
string += _("%d hours, ") % hour
if (min == 1):
string += _(u"%d minute, ") % min
string += _("%d minute, ") % min
elif (min >= 2):
string += _(u"%d minutes, ") % min
string += _("%d minutes, ") % min
if sec >= 0 and sec <= 2:
string += _(u"%s second") % sec_string
string += _("%s second") % sec_string
else:
string += _(u"%s seconds") % sec_string
string += _("%s seconds") % sec_string
return string
def find_urls_in_text(text):
@@ -40,7 +42,7 @@ def find_urls_in_text(text):
def download_file(url, local_filename, window):
r = requests.get(url, stream=True)
window.change_status(_(u"Downloading {0}").format(local_filename,))
window.change_status(_("Downloading {0}").format(local_filename,))
total_length = r.headers.get("content-length")
dl = 0
total_length = int(total_length)
@@ -49,42 +51,9 @@ def download_file(url, local_filename, window):
if chunk: # filter out keep-alive new chunks
dl += len(chunk)
f.write(chunk)
done = int(100 * dl / total_length)
msg = _(u"Downloading {0} ({1}%)").format(os.path.basename(local_filename), done)
done = int(100 * dl/total_length)
msg = _("Downloading {0} ({1}%)").format(os.path.basename(local_filename), done)
window.change_status(msg)
window.change_status(_(u"Ready"))
window.change_status(_("Ready"))
return local_filename
def clean_text(text):
""" Replaces all HTML entities and put the plain text equivalent if it's possible."""
text = text.replace("<br>", "\n")
text = text.replace("\\n", "\n")
return text
def add_attachment(attachment):
msg = u""
tpe = ""
if attachment["type"] == "link":
msg = u"{0}: {1}".format(attachment["link"]["title"], attachment["link"]["url"])
tpe = _(u"Link")
elif attachment["type"] == "photo":
tpe = _(u"Photo")
msg = attachment["photo"]["text"]
if msg == "":
msg = _(u"no description available")
elif attachment["type"] == "video":
msg = u"{0}".format(attachment["video"]["title"],)
tpe = _(u"Video")
elif attachment["type"] == "audio":
msg = u"{0}".format(" ".join(renderers.render_audio(attachment["audio"])))
tpe = _(u"Audio")
elif attachment["type"] == "doc":
msg = u"{0}".format(attachment["doc"]["title"])
tpe = _(u"{0} file").format(attachment["doc"]["ext"])
elif attachment["type"] == "audio_message":
msg = u"{0}".format(" ".join(renderers.render_audio_message(attachment["audio_message"])))
tpe = _(u"Voice message")
else:
print attachment
return [tpe, msg]

View File

@@ -1,9 +1,9 @@
#!/usr/bin/python
import keys
import logging
import core
from . import core
from vk_api.audio import VkAudio
from wxUI import two_factor_auth
from . wxUI import two_factor_auth
log = logging.getLogger("vkSessionHandler")
@@ -16,7 +16,7 @@ class vkObject(object):
if alt_token == False:
log.info("Using kate's token...")
# Let's import the patched vk_api module for using a different user agent
import vk_api_patched as vk_api
from . import vk_api_patched as vk_api
if token == "" or token == None:
log.info("Token is not valid. Generating one...")
token = core.requestAuth(user, password)

View File

@@ -5,7 +5,7 @@ import logging
import vk_api
import threading
import requests
import jconfig_patched as jconfig
from . import jconfig_patched as jconfig
from vk_api.enums import VkUserPermissions
from vk_api.exceptions import *

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import wx
import widgetUtils
@@ -7,7 +8,7 @@ code = None
remember = True
def new_account_dialog():
return wx.MessageDialog(None, _(u"In order to continue, you need to configure your VK account before. Would you like to autorhise a new account now?"), _(u"Authorisation"), wx.YES_NO).ShowModal()
return wx.MessageDialog(None, _("In order to continue, you need to configure your VK account before. Would you like to autorhise a new account now?"), _("Authorisation"), wx.YES_NO).ShowModal()
def two_factor_auth():
global code, remember
@@ -18,7 +19,7 @@ def two_factor_auth():
def get_code():
global code, remember
dlg = wx.TextEntryDialog(None, _(u"Please provide the authentication code you have received from VK."), _(u"Two factor authentication code"))
dlg = wx.TextEntryDialog(None, _("Please provide the authentication code you have received from VK."), _("Two factor authentication code"))
response = dlg.ShowModal()
if response == widgetUtils.OK:
code = dlg.GetValue()
@@ -27,11 +28,11 @@ def get_code():
class newSessionDialog(widgetUtils.BaseDialog):
def __init__(self):
super(newSessionDialog, self).__init__(parent=None, id=wx.NewId(), title=_(u"Authorise VK"))
super(newSessionDialog, self).__init__(parent=None, id=wx.NewId(), title=_("Authorise VK"))
panel = wx.Panel(self)
lbl1 = wx.StaticText(panel, -1, _(u"&Email or phone number"))
lbl1 = wx.StaticText(panel, -1, _("&Email or phone number"))
self.email = wx.TextCtrl(panel, -1)
lbl2 = wx.StaticText(panel, -1, _(u"&Password"))
lbl2 = wx.StaticText(panel, -1, _("&Password"))
self.passw = wx.TextCtrl(panel, -1, style=wx.TE_PASSWORD)
sizer = wx.BoxSizer()
b1 = wx.BoxSizer(wx.HORIZONTAL)