From 2de4eea35744f7e6ef6aa82b6beba35a3cae38da Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 26 Jan 2018 23:21:46 +0300 Subject: [PATCH] initial commit - rewriting. ngc wrapper added, project structure updated --- tests/tests.py | 18 +- toxygen/app.py | 263 +++++ toxygen/av/__init__.py | 0 toxygen/av/call.py | 58 ++ toxygen/{ => av}/calls.py | 66 +- toxygen/{ => av}/screen_sharing.py | 0 toxygen/bootstrap/__init__.py | 0 toxygen/{ => bootstrap}/bootstrap.py | 2 +- toxygen/{ => bootstrap}/nodes.json | 0 toxygen/callbacks.py | 12 +- toxygen/contacts/__init__.py | 0 toxygen/{ => contacts}/basecontact.py | 10 +- toxygen/{ => contacts}/contact.py | 7 +- toxygen/{ => contacts}/friend.py | 4 +- toxygen/{ => contacts}/group_chat.py | 6 +- toxygen/{ => contacts}/profile.py | 37 +- toxygen/db/__init__.py | 0 toxygen/{ => db}/history.py | 30 +- toxygen/file_transfers.py | 8 +- toxygen/login.py | 0 toxygen/main.py | 434 +------- toxygen/messenger/__init__.py | 0 toxygen/{ => messenger}/messages.py | 0 toxygen/network/__init__.py | 0 toxygen/{ => network}/tox_dns.py | 2 +- toxygen/plugin_support/__init__.py | 0 .../{ => plugin_support}/plugin_support.py | 4 +- .../{smileys.py => smileys_and_stickers.py} | 0 toxygen/threads.py | 62 ++ toxygen/toxcore_enums_and_consts.py | 220 ---- toxygen/tray.py | 83 ++ toxygen/ui/__init__.py | 0 toxygen/{ => ui}/avwidgets.py | 6 +- toxygen/{ => ui}/items_factory.py | 3 +- toxygen/{ => ui}/list_items.py | 12 +- toxygen/{ => ui}/loginscreen.py | 3 +- toxygen/{ => ui}/mainscreen.py | 13 +- toxygen/{ => ui}/mainscreen_widgets.py | 6 +- toxygen/{ => ui}/menu.py | 14 +- toxygen/{ => ui}/passwordscreen.py | 2 +- toxygen/{ => ui}/widgets.py | 0 toxygen/updater/__init__.py | 0 toxygen/{ => updater}/updater.py | 2 +- toxygen/user_data/__init__.py | 0 toxygen/user_data/profile_manager.py | 70 ++ toxygen/{ => user_data}/settings.py | 82 +- toxygen/{ => user_data}/toxes.py | 10 +- toxygen/util/__init__.py | 0 toxygen/{ => util}/util.py | 2 +- toxygen/wrapper/__init__.py | 0 toxygen/{ => wrapper}/libtox.py | 0 toxygen/{ => wrapper}/tox.py | 874 ++++++++++++++-- toxygen/{ => wrapper}/toxav.py | 4 +- toxygen/{ => wrapper}/toxav_enums.py | 0 toxygen/wrapper/toxcore_enums_and_consts.py | 944 ++++++++++++++++++ toxygen/{ => wrapper}/toxencryptsave.py | 4 +- .../toxencryptsave_enums_and_consts.py | 0 57 files changed, 2420 insertions(+), 957 deletions(-) create mode 100644 toxygen/app.py create mode 100644 toxygen/av/__init__.py create mode 100644 toxygen/av/call.py rename toxygen/{ => av}/calls.py (85%) rename toxygen/{ => av}/screen_sharing.py (100%) create mode 100644 toxygen/bootstrap/__init__.py rename toxygen/{ => bootstrap}/bootstrap.py (98%) rename toxygen/{ => bootstrap}/nodes.json (100%) create mode 100644 toxygen/contacts/__init__.py rename toxygen/{ => contacts}/basecontact.py (92%) rename toxygen/{ => contacts}/contact.py (99%) rename toxygen/{ => contacts}/friend.py (97%) rename toxygen/{ => contacts}/group_chat.py (94%) rename toxygen/{ => contacts}/profile.py (98%) create mode 100644 toxygen/db/__init__.py rename toxygen/{ => db}/history.py (88%) create mode 100644 toxygen/login.py create mode 100644 toxygen/messenger/__init__.py rename toxygen/{ => messenger}/messages.py (100%) create mode 100644 toxygen/network/__init__.py rename toxygen/{ => network}/tox_dns.py (98%) create mode 100644 toxygen/plugin_support/__init__.py rename toxygen/{ => plugin_support}/plugin_support.py (99%) rename toxygen/{smileys.py => smileys_and_stickers.py} (100%) create mode 100644 toxygen/threads.py delete mode 100644 toxygen/toxcore_enums_and_consts.py create mode 100644 toxygen/tray.py create mode 100644 toxygen/ui/__init__.py rename toxygen/{ => ui}/avwidgets.py (98%) rename toxygen/{ => ui}/items_factory.py (97%) rename toxygen/{ => ui}/list_items.py (99%) rename toxygen/{ => ui}/loginscreen.py (98%) rename toxygen/{ => ui}/mainscreen.py (99%) rename toxygen/{ => ui}/mainscreen_widgets.py (99%) rename toxygen/{ => ui}/menu.py (99%) rename toxygen/{ => ui}/passwordscreen.py (99%) rename toxygen/{ => ui}/widgets.py (100%) create mode 100644 toxygen/updater/__init__.py rename toxygen/{ => updater}/updater.py (99%) create mode 100644 toxygen/user_data/__init__.py create mode 100644 toxygen/user_data/profile_manager.py rename toxygen/{ => user_data}/settings.py (72%) rename toxygen/{ => user_data}/toxes.py (77%) create mode 100644 toxygen/util/__init__.py rename toxygen/{ => util}/util.py (98%) create mode 100644 toxygen/wrapper/__init__.py rename toxygen/{ => wrapper}/libtox.py (100%) rename toxygen/{ => wrapper}/tox.py (68%) rename toxygen/{ => wrapper}/toxav.py (99%) rename toxygen/{ => wrapper}/toxav_enums.py (100%) create mode 100644 toxygen/wrapper/toxcore_enums_and_consts.py rename toxygen/{ => wrapper}/toxencryptsave.py (97%) rename toxygen/{ => wrapper}/toxencryptsave_enums_and_consts.py (100%) diff --git a/tests/tests.py b/tests/tests.py index 27618af..cad1d1c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,9 +1,9 @@ -from toxygen.profile import * -from toxygen.tox_dns import tox_dns -from toxygen.history import History +from contacts.profile import * +from network.tox_dns import tox_dns +from db.history import History from toxygen.smileys import SmileyLoader -from toxygen.messages import * -import toxygen.toxes as encr +from messenger.messages import * +import user_data.toxes as encr import toxygen.util as util import time @@ -23,15 +23,15 @@ class TestTox: assert tox.self_get_status_message() == str(status_message, 'utf-8') -class TestProfileHelper: +class TestProfileManager: def test_creation(self): file_name, path = 'test.tox', os.path.dirname(os.path.realpath(__file__)) + '/' data = b'test' with open(path + file_name, 'wb') as fl: fl.write(data) - ph = ProfileHelper(path, file_name[:4]) - assert ProfileHelper.get_path() == path + ph = ProfileManager(path, file_name[:4]) + assert ProfileManager.get_path() == path assert ph.open_profile() == data assert os.path.exists(path + 'avatars/') @@ -81,7 +81,7 @@ def create_singletons(): Settings._instance = Settings.get_default_settings() if not os.path.exists(folder): os.makedirs(folder) - ProfileHelper(folder, 'test') + ProfileManager(folder, 'test') def create_friend(name, status_message, number, tox_id): diff --git a/toxygen/app.py b/toxygen/app.py new file mode 100644 index 0000000..1dec36f --- /dev/null +++ b/toxygen/app.py @@ -0,0 +1,263 @@ +class App: + + def __init__(self, path_or_uri=None): + self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None + if path_or_uri is None: + self.uri = self.path = None + elif path_or_uri.startswith('tox:'): + self.path = None + self.uri = path_or_uri[4:] + else: + self.path = path_or_uri + self.uri = None + + def enter_pass(self, data): + """ + Show password screen + """ + tmp = [data] + p = PasswordScreen(toxes.ToxES.get_instance(), tmp) + p.show() + self.app.lastWindowClosed.connect(self.app.quit) + self.app.exec_() + if tmp[0] == data: + raise SystemExit() + else: + return tmp[0] + + def main(self): + """ + Main function of app. loads login screen if needed and starts main screen + """ + app = QtWidgets.QApplication(sys.argv) + app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + self.app = app + + if platform.system() == 'Linux': + QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) + + with open(curr_directory() + '/styles/dark_style.qss') as fl: + style = fl.read() + app.setStyleSheet(style) + + encrypt_save = toxes.ToxES() + + if self.path is not None: + path = os.path.dirname(self.path) + '/' + name = os.path.basename(self.path)[:-4] + data = ProfileManager(path, name).open_profile() + if encrypt_save.is_data_encrypted(data): + data = self.enter_pass(data) + settings = Settings(name) + self.tox = profile.tox_factory(data, settings) + else: + auto_profile = Settings.get_auto_profile() + if not auto_profile[0]: + # show login screen if default profile not found + current_locale = QtCore.QLocale() + curr_lang = current_locale.languageToString(current_locale.language()) + langs = Settings.supported_languages() + if curr_lang in langs: + lang_path = langs[curr_lang] + translator = QtCore.QTranslator() + translator.load(curr_directory() + '/translations/' + lang_path) + app.installTranslator(translator) + app.translator = translator + ls = LoginScreen() + ls.setWindowIconText("Toxygen") + profiles = ProfileManager.find_profiles() + ls.update_select(map(lambda x: x[1], profiles)) + _login = self.Login(profiles) + ls.update_on_close(_login.login_screen_close) + ls.show() + app.exec_() + if not _login.t: + return + elif _login.t == 1: # create new profile + _login.name = _login.name.strip() + name = _login.name if _login.name else 'toxygen_user' + pr = map(lambda x: x[1], ProfileManager.find_profiles()) + if name in list(pr): + msgBox = QtWidgets.QMessageBox() + msgBox.setWindowTitle( + QtWidgets.QApplication.translate("MainWindow", "Error")) + text = (QtWidgets.QApplication.translate("MainWindow", + 'Profile with this name already exists')) + msgBox.setText(text) + msgBox.exec_() + return + self.tox = profile.tox_factory() + self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User') + self.tox.self_set_status_message(b'Toxing on Toxygen') + reply = QtWidgets.QMessageBox.question(None, + 'Profile {}'.format(name), + QtWidgets.QApplication.translate("login", + 'Do you want to set profile password?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + set_pass = SetProfilePasswordScreen(encrypt_save) + set_pass.show() + self.app.lastWindowClosed.connect(self.app.quit) + self.app.exec_() + reply = QtWidgets.QMessageBox.question(None, + 'Profile {}'.format(name), + QtWidgets.QApplication.translate("login", + 'Do you want to save profile in default folder? If no, profile will be saved in program folder'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + path = Settings.get_default_path() + else: + path = curr_directory() + '/' + try: + ProfileManager(path, name).save_profile(self.tox.get_savedata()) + except Exception as ex: + print(str(ex)) + log('Profile creation exception: ' + str(ex)) + msgBox = QtWidgets.QMessageBox() + msgBox.setText(QtWidgets.QApplication.translate("login", + 'Profile saving error! Does Toxygen have permission to write to this directory?')) + msgBox.exec_() + return + path = Settings.get_default_path() + settings = Settings(name) + if curr_lang in langs: + settings['language'] = curr_lang + settings.save() + else: # load existing profile + path, name = _login.get_data() + if _login.default: + Settings.set_auto_profile(path, name) + data = ProfileManager(path, name).open_profile() + if encrypt_save.is_data_encrypted(data): + data = self.enter_pass(data) + settings = Settings(name) + self.tox = profile.tox_factory(data, settings) + else: + path, name = auto_profile + data = ProfileManager(path, name).open_profile() + if encrypt_save.is_data_encrypted(data): + data = self.enter_pass(data) + settings = Settings(name) + self.tox = profile.tox_factory(data, settings) + + if Settings.is_active_profile(path, name): # profile is in use + reply = QtWidgets.QMessageBox.question(None, + 'Profile {}'.format(name), + QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply != QtWidgets.QMessageBox.Yes: + return + else: + settings.set_active_profile() + + # application color scheme + for theme in settings.built_in_themes().keys(): + if settings['theme'] == theme: + with open(curr_directory() + settings.built_in_themes()[theme]) as fl: + style = fl.read() + app.setStyleSheet(style) + + lang = Settings.supported_languages()[settings['language']] + translator = QtCore.QTranslator() + translator.load(curr_directory() + '/translations/' + lang) + app.installTranslator(translator) + app.translator = translator + + # tray icon + + + self.ms.show() + + updating = False + if settings['update'] and updater.updater_available() and updater.connection_available(): # auto update + version = updater.check_for_updates() + if version is not None: + if settings['update'] == 2: + updater.download(version) + updating = True + else: + reply = QtWidgets.QMessageBox.question(None, + 'Toxygen', + QtWidgets.QApplication.translate("login", + 'Update for Toxygen was found. Download and install it?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + updater.download(version) + updating = True + + if updating: + data = self.tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + settings.close() + del self.tox + return + + plugin_helper = PluginLoader(self.tox, settings) # plugin support + plugin_helper.load() + + start() + # init thread + self.init = self.InitThread(self.tox, self.ms, self.tray) + self.init.start() + + # starting threads for tox iterate and toxav iterate + self.mainloop = self.ToxIterateThread(self.tox) + self.mainloop.start() + self.avloop = self.ToxAVIterateThread(self.tox.AV) + self.avloop.start() + + if self.uri is not None: + self.ms.add_contact(self.uri) + + app.lastWindowClosed.connect(app.quit) + app.exec_() + + self.init.stop = True + self.mainloop.stop = True + self.avloop.stop = True + plugin_helper.stop() + stop() + self.mainloop.wait() + self.init.wait() + self.avloop.wait() + self.tray.hide() + data = self.tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + settings.close() + del self.tox + + def reset(self): + """ + Create new tox instance (new network settings) + :return: tox instance + """ + self.mainloop.stop = True + self.init.stop = True + self.avloop.stop = True + self.mainloop.wait() + self.init.wait() + self.avloop.wait() + data = self.tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + del self.tox + # create new tox instance + self.tox = profile.tox_factory(data, Settings.get_instance()) + # init thread + self.init = self.InitThread(self.tox, self.ms, self.tray) + self.init.start() + + # starting threads for tox iterate and toxav iterate + self.mainloop = self.ToxIterateThread(self.tox) + self.mainloop.start() + + self.avloop = self.ToxAVIterateThread(self.tox.AV) + self.avloop.start() + + plugin_helper = PluginLoader.get_instance() + plugin_helper.set_tox(self.tox) + + return self.tox \ No newline at end of file diff --git a/toxygen/av/__init__.py b/toxygen/av/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/av/call.py b/toxygen/av/call.py new file mode 100644 index 0000000..c43e264 --- /dev/null +++ b/toxygen/av/call.py @@ -0,0 +1,58 @@ + + +class Call: + + def __init__(self, out_audio, out_video, in_audio=False, in_video=False): + self._in_audio = in_audio + self._in_video = in_video + self._out_audio = out_audio + self._out_video = out_video + self._is_active = False + + def get_is_active(self): + return self._is_active + + def set_is_active(self, value): + self._is_active = value + + is_active = property(get_is_active, set_is_active) + + # ----------------------------------------------------------------------------------------------------------------- + # Audio + # ----------------------------------------------------------------------------------------------------------------- + + def get_in_audio(self): + return self._in_audio + + def set_in_audio(self, value): + self._in_audio = value + + in_audio = property(get_in_audio, set_in_audio) + + def get_out_audio(self): + return self._out_audio + + def set_out_audio(self, value): + self._out_audio = value + + out_audio = property(get_out_audio, set_out_audio) + + # ----------------------------------------------------------------------------------------------------------------- + # Video + # ----------------------------------------------------------------------------------------------------------------- + + def get_in_video(self): + return self._in_video + + def set_in_video(self, value): + self._in_video = value + + in_video = property(get_in_video, set_in_video) + + def get_out_video(self): + return self._out_video + + def set_out_video(self, value): + self._in_video = value + + out_video = property(get_out_video, set_out_video) diff --git a/toxygen/calls.py b/toxygen/av/calls.py similarity index 85% rename from toxygen/calls.py rename to toxygen/av/calls.py index 6477c03..20684b2 100644 --- a/toxygen/calls.py +++ b/toxygen/av/calls.py @@ -1,73 +1,17 @@ import pyaudio import time import threading -import settings -from toxav_enums import * +from user_data import settings +from wrapper.toxav_enums import * import cv2 import itertools import numpy as np -import screen_sharing +from av import screen_sharing + + # TODO: play sound until outgoing call will be started or cancelled -class Call: - - def __init__(self, out_audio, out_video, in_audio=False, in_video=False): - self._in_audio = in_audio - self._in_video = in_video - self._out_audio = out_audio - self._out_video = out_video - self._is_active = False - - def get_is_active(self): - return self._is_active - - def set_is_active(self, value): - self._is_active = value - - is_active = property(get_is_active, set_is_active) - - # ----------------------------------------------------------------------------------------------------------------- - # Audio - # ----------------------------------------------------------------------------------------------------------------- - - def get_in_audio(self): - return self._in_audio - - def set_in_audio(self, value): - self._in_audio = value - - in_audio = property(get_in_audio, set_in_audio) - - def get_out_audio(self): - return self._out_audio - - def set_out_audio(self, value): - self._out_audio = value - - out_audio = property(get_out_audio, set_out_audio) - - # ----------------------------------------------------------------------------------------------------------------- - # Video - # ----------------------------------------------------------------------------------------------------------------- - - def get_in_video(self): - return self._in_video - - def set_in_video(self, value): - self._in_video = value - - in_video = property(get_in_video, set_in_video) - - def get_out_video(self): - return self._out_video - - def set_out_video(self, value): - self._in_video = value - - out_video = property(get_out_video, set_out_video) - - class AV: def __init__(self, toxav): diff --git a/toxygen/screen_sharing.py b/toxygen/av/screen_sharing.py similarity index 100% rename from toxygen/screen_sharing.py rename to toxygen/av/screen_sharing.py diff --git a/toxygen/bootstrap/__init__.py b/toxygen/bootstrap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/bootstrap.py b/toxygen/bootstrap/bootstrap.py similarity index 98% rename from toxygen/bootstrap.py rename to toxygen/bootstrap/bootstrap.py index 0589940..6d97f25 100644 --- a/toxygen/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,7 +1,7 @@ import random import urllib.request from util import log, curr_directory -import settings +from user_data import settings from PyQt5 import QtNetwork, QtCore import json diff --git a/toxygen/nodes.json b/toxygen/bootstrap/nodes.json similarity index 100% rename from toxygen/nodes.json rename to toxygen/bootstrap/nodes.json diff --git a/toxygen/callbacks.py b/toxygen/callbacks.py index b59d17c..ba2994d 100644 --- a/toxygen/callbacks.py +++ b/toxygen/callbacks.py @@ -1,10 +1,10 @@ -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtGui from notifications import * -from settings import Settings -from profile import Profile -from toxcore_enums_and_consts import * -from toxav_enums import * -from tox import bin_to_string +from user_data.settings import Settings +from contacts.profile import Profile +from wrapper.toxcore_enums_and_consts import * +from wrapper.toxav_enums import * +from wrapper.tox import bin_to_string from plugin_support import PluginLoader import queue import threading diff --git a/toxygen/contacts/__init__.py b/toxygen/contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/basecontact.py b/toxygen/contacts/basecontact.py similarity index 92% rename from toxygen/basecontact.py rename to toxygen/contacts/basecontact.py index e1243a4..6e2d55a 100644 --- a/toxygen/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -1,6 +1,6 @@ -from settings import * +from user_data.settings import * from PyQt5 import QtCore, QtGui -from toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE +from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE class BaseContact: @@ -81,7 +81,7 @@ class BaseContact: """ Tries to load avatar of contact or uses default avatar """ - prefix = ProfileHelper.get_path() + 'avatars/' + prefix = ProfileManager.get_path() + 'avatars/' avatar_path = prefix + '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image avatar_path = curr_directory() + '/images/avatar.png' @@ -92,13 +92,13 @@ class BaseContact: self._widget.avatar_label.repaint() def reset_avatar(self): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if os.path.isfile(avatar_path): os.remove(avatar_path) self.load_avatar() def set_avatar(self, avatar): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) with open(avatar_path, 'wb') as f: f.write(avatar) self.load_avatar() diff --git a/toxygen/contact.py b/toxygen/contacts/contact.py similarity index 99% rename from toxygen/contact.py rename to toxygen/contacts/contact.py index 9f27a1d..ad491fd 100644 --- a/toxygen/contact.py +++ b/toxygen/contacts/contact.py @@ -1,8 +1,7 @@ -from PyQt5 import QtCore, QtGui -from history import * -import basecontact +from db.history import * +from contacts import basecontact import util -from messages import * +from messenger.messages import * import file_transfers as ft import re diff --git a/toxygen/friend.py b/toxygen/contacts/friend.py similarity index 97% rename from toxygen/friend.py rename to toxygen/contacts/friend.py index d912708..b80032a 100644 --- a/toxygen/friend.py +++ b/toxygen/contacts/friend.py @@ -1,5 +1,5 @@ -import contact -from messages import * +from contacts import contact +from messenger.messages import * import os diff --git a/toxygen/group_chat.py b/toxygen/contacts/group_chat.py similarity index 94% rename from toxygen/group_chat.py rename to toxygen/contacts/group_chat.py index f7921a1..05faaa9 100644 --- a/toxygen/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,9 +1,11 @@ -import contact +from contacts import contact import util from PyQt5 import QtGui, QtCore -import toxcore_enums_and_consts as constants +from wrapper import toxcore_enums_and_consts as constants +# TODO: ngc + class GroupChat(contact.Contact): def __init__(self, name, status_message, widget, tox, group_number): diff --git a/toxygen/profile.py b/toxygen/contacts/profile.py similarity index 98% rename from toxygen/profile.py rename to toxygen/contacts/profile.py index 204419a..f74d134 100644 --- a/toxygen/profile.py +++ b/toxygen/contacts/profile.py @@ -1,22 +1,21 @@ -from list_items import * -from PyQt5 import QtGui, QtWidgets -from friend import * -from settings import * -from toxcore_enums_and_consts import * +from ui.list_items import * +from PyQt5 import QtWidgets +from contacts.friend import * +from user_data.settings import * +from wrapper.toxcore_enums_and_consts import * from ctypes import * from util import log, Singleton, curr_directory -from tox_dns import tox_dns -from history import * +from network.tox_dns import tox_dns +from db.history import * from file_transfers import * import time -import calls -import avwidgets +from av import calls import plugin_support -import basecontact -import items_factory +from contacts import basecontact +from ui import items_factory, avwidgets import cv2 import threading -from group_chat import * +from contacts.group_chat import * import re @@ -283,7 +282,7 @@ class Profile(basecontact.BaseContact, Singleton): if friend.tox_id is None: avatar_path = curr_directory() + '/images/group.png' else: - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # load default image avatar_path = curr_directory() + '/images/avatar.png' os.chdir(os.path.dirname(avatar_path)) @@ -752,7 +751,7 @@ class Profile(basecontact.BaseContact, Singleton): else: self.set_active(0) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) def add_friend(self, tox_id): """ @@ -785,7 +784,7 @@ class Profile(basecontact.BaseContact, Singleton): num = self._tox.friend_by_public_key(tox_id) self.delete_friend(num) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) except: # not in friend list pass @@ -801,7 +800,7 @@ class Profile(basecontact.BaseContact, Singleton): if add_to_friend_list: self.add_friend(tox_id) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) # ----------------------------------------------------------------------------------------------------------------- # Friend requests @@ -837,7 +836,7 @@ class Profile(basecontact.BaseContact, Singleton): friend = Friend(message_getter, result, tox_id, '', item, tox_id) self._contacts.append(friend) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) return True except Exception as ex: # wrong data log('Friend request failed with ' + str(ex)) @@ -857,7 +856,7 @@ class Profile(basecontact.BaseContact, Singleton): if reply == QtWidgets.QMessageBox.Yes: # accepted self.add_friend(tox_id) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) except Exception as ex: # something is wrong log('Accept friend request failed! ' + str(ex)) @@ -1180,7 +1179,7 @@ class Profile(basecontact.BaseContact, Singleton): """ :param friend_number: number of friend who should get new avatar """ - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # reset image avatar_path = None sa = SendAvatar(avatar_path, self._tox, friend_number) diff --git a/toxygen/db/__init__.py b/toxygen/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/history.py b/toxygen/db/history.py similarity index 88% rename from toxygen/history.py rename to toxygen/db/history.py index 586981a..eea7457 100644 --- a/toxygen/history.py +++ b/toxygen/db/history.py @@ -1,8 +1,8 @@ from sqlite3 import connect -import settings +from user_data import settings from os import chdir import os.path -from toxes import ToxES +from user_data.toxes import ToxES PAGE_SIZE = 42 @@ -17,13 +17,15 @@ MESSAGE_OWNER = { 'NOT_SENT': 2 } +# TODO: unique message id and ngc support + class History: def __init__(self, name): self._name = name - chdir(settings.ProfileHelper.get_path()) - path = settings.ProfileHelper.get_path() + self._name + '.hstr' + chdir(settings.ProfileManager.get_path()) + path = settings.ProfileManager.get_path() + self._name + '.hstr' if os.path.exists(path): decr = ToxES.get_instance() try: @@ -45,7 +47,7 @@ class History: def save(self): encr = ToxES.get_instance() if encr.has_password(): - path = settings.ProfileHelper.get_path() + self._name + '.hstr' + path = settings.ProfileManager.get_path() + self._name + '.hstr' with open(path, 'rb') as fin: data = fin.read() data = encr.pass_encrypt(bytes(data)) @@ -53,7 +55,7 @@ class History: fout.write(data) def export(self, directory): - path = settings.ProfileHelper.get_path() + self._name + '.hstr' + path = settings.ProfileManager.get_path() + self._name + '.hstr' new_path = directory + self._name + '.hstr' with open(path, 'rb') as fin: data = fin.read() @@ -64,7 +66,7 @@ class History: fout.write(data) def add_friend_to_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -84,7 +86,7 @@ class History: db.close() def delete_friend_from_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -98,7 +100,7 @@ class History: db.close() def friend_exists_in_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) cursor = db.cursor() cursor.execute('SELECT 0 FROM friends WHERE tox_id=?', (tox_id, )) @@ -107,7 +109,7 @@ class History: return result is not None def save_messages_to_db(self, tox_id, messages_iter): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -121,7 +123,7 @@ class History: db.close() def update_messages(self, tox_id, unsent_time): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -136,7 +138,7 @@ class History: def delete_message(self, tox_id, time): start, end = str(time - 0.01), str(time + 0.01) - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -150,7 +152,7 @@ class History: db.close() def delete_messages(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -174,7 +176,7 @@ class History: self._db = self._cursor = None def connect(self): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) self._db = connect(self._name + '.hstr', timeout=TIMEOUT) self._cursor = self._db.cursor() self._cursor.execute('SELECT message, owner, unix_time, message_type FROM id' + self._tox_id + diff --git a/toxygen/file_transfers.py b/toxygen/file_transfers.py index 7e0b193..6c65809 100644 --- a/toxygen/file_transfers.py +++ b/toxygen/file_transfers.py @@ -1,9 +1,9 @@ -from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL +from wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL from os.path import basename, getsize, exists, dirname from os import remove, rename, chdir from time import time, sleep -from tox import Tox -import settings +from wrapper.tox import Tox +from user_data import settings from PyQt5 import QtCore @@ -305,7 +305,7 @@ class ReceiveAvatar(ReceiveTransfer): MAX_AVATAR_SIZE = 512 * 1024 def __init__(self, tox, friend_number, size, file_number): - path = settings.ProfileHelper.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) + path = settings.ProfileManager.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) super(ReceiveAvatar, self).__init__(path + '.tmp', tox, friend_number, size, file_number) if size > self.MAX_AVATAR_SIZE: self.send_control(TOX_FILE_CONTROL['CANCEL']) diff --git a/toxygen/login.py b/toxygen/login.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/main.py b/toxygen/main.py index d630bb6..4e91c50 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,434 +1,11 @@ import sys -from loginscreen import LoginScreen -import profile -from settings import * -from PyQt5 import QtCore, QtGui, QtWidgets -from bootstrap import generate_nodes, download_nodes_list -from mainscreen import MainWindow +from user_data.settings import * from callbacks import init_callbacks, stop, start from util import curr_directory, program_version, remove -import styles.style # reqired for styles loading -import platform -import toxes -from passwordscreen import PasswordScreen, UnlockAppScreen, SetProfilePasswordScreen -from plugin_support import PluginLoader import updater +import argparse -class Toxygen: - - def __init__(self, path_or_uri=None): - super(Toxygen, self).__init__() - self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None - if path_or_uri is None: - self.uri = self.path = None - elif path_or_uri.startswith('tox:'): - self.path = None - self.uri = path_or_uri[4:] - else: - self.path = path_or_uri - self.uri = None - - def enter_pass(self, data): - """ - Show password screen - """ - tmp = [data] - p = PasswordScreen(toxes.ToxES.get_instance(), tmp) - p.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - if tmp[0] == data: - raise SystemExit() - else: - return tmp[0] - - def main(self): - """ - Main function of app. loads login screen if needed and starts main screen - """ - app = QtWidgets.QApplication(sys.argv) - app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.app = app - - if platform.system() == 'Linux': - QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - - with open(curr_directory() + '/styles/dark_style.qss') as fl: - style = fl.read() - app.setStyleSheet(style) - - encrypt_save = toxes.ToxES() - - if self.path is not None: - path = os.path.dirname(self.path) + '/' - name = os.path.basename(self.path)[:-4] - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - else: - auto_profile = Settings.get_auto_profile() - if not auto_profile[0]: - # show login screen if default profile not found - current_locale = QtCore.QLocale() - curr_lang = current_locale.languageToString(current_locale.language()) - langs = Settings.supported_languages() - if curr_lang in langs: - lang_path = langs[curr_lang] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang_path) - app.installTranslator(translator) - app.translator = translator - ls = LoginScreen() - ls.setWindowIconText("Toxygen") - profiles = ProfileHelper.find_profiles() - ls.update_select(map(lambda x: x[1], profiles)) - _login = self.Login(profiles) - ls.update_on_close(_login.login_screen_close) - ls.show() - app.exec_() - if not _login.t: - return - elif _login.t == 1: # create new profile - _login.name = _login.name.strip() - name = _login.name if _login.name else 'toxygen_user' - pr = map(lambda x: x[1], ProfileHelper.find_profiles()) - if name in list(pr): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Error")) - text = (QtWidgets.QApplication.translate("MainWindow", - 'Profile with this name already exists')) - msgBox.setText(text) - msgBox.exec_() - return - self.tox = profile.tox_factory() - self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User') - self.tox.self_set_status_message(b'Toxing on Toxygen') - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to set profile password?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - set_pass = SetProfilePasswordScreen(encrypt_save) - set_pass.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to save profile in default folder? If no, profile will be saved in program folder'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - path = Settings.get_default_path() - else: - path = curr_directory() + '/' - try: - ProfileHelper(path, name).save_profile(self.tox.get_savedata()) - except Exception as ex: - print(str(ex)) - log('Profile creation exception: ' + str(ex)) - msgBox = QtWidgets.QMessageBox() - msgBox.setText(QtWidgets.QApplication.translate("login", - 'Profile saving error! Does Toxygen have permission to write to this directory?')) - msgBox.exec_() - return - path = Settings.get_default_path() - settings = Settings(name) - if curr_lang in langs: - settings['language'] = curr_lang - settings.save() - else: # load existing profile - path, name = _login.get_data() - if _login.default: - Settings.set_auto_profile(path, name) - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - else: - path, name = auto_profile - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - - if Settings.is_active_profile(path, name): # profile is in use - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply != QtWidgets.QMessageBox.Yes: - return - else: - settings.set_active_profile() - - # application color scheme - for theme in settings.built_in_themes().keys(): - if settings['theme'] == theme: - with open(curr_directory() + settings.built_in_themes()[theme]) as fl: - style = fl.read() - app.setStyleSheet(style) - - lang = Settings.supported_languages()[settings['language']] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang) - app.installTranslator(translator) - app.translator = translator - - # tray icon - self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.tray.setObjectName('tray') - - self.ms = MainWindow(self.tox, self.reset, self.tray) - app.aboutToQuit.connect(self.ms.close_window) - - class Menu(QtWidgets.QMenu): - - def newStatus(self, status): - if not Settings.get_instance().locked: - profile.Profile.get_instance().set_status(status) - self.aboutToShowHandler() - self.hide() - - def aboutToShowHandler(self): - status = profile.Profile.get_instance().status - act = self.act - if status is None or Settings.get_instance().locked: - self.actions()[1].setVisible(False) - else: - self.actions()[1].setVisible(True) - act.actions()[0].setChecked(False) - act.actions()[1].setChecked(False) - act.actions()[2].setChecked(False) - act.actions()[status].setChecked(True) - self.actions()[2].setVisible(not Settings.get_instance().locked) - - def languageChange(self, *args, **kwargs): - self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) - self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) - self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) - self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away')) - self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy')) - - m = Menu() - show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) - onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) - away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) - busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) - onl.setCheckable(True) - away.setCheckable(True) - busy.setCheckable(True) - m.act = sub - exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) - - def show_window(): - s = Settings.get_instance() - - def show(): - if not self.ms.isActiveWindow(): - self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) - self.ms.activateWindow() - self.ms.show() - if not s.locked: - show() - else: - def correct_pass(): - show() - s.locked = False - s.unlockScreen = False - if not s.unlockScreen: - s.unlockScreen = True - self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) - self.p.show() - - def tray_activated(reason): - if reason == QtWidgets.QSystemTrayIcon.DoubleClick: - show_window() - - def close_app(): - if not Settings.get_instance().locked: - settings.closing = True - self.ms.close() - - show.triggered.connect(show_window) - exit.triggered.connect(close_app) - m.aboutToShow.connect(lambda: m.aboutToShowHandler()) - onl.triggered.connect(lambda: m.newStatus(0)) - away.triggered.connect(lambda: m.newStatus(1)) - busy.triggered.connect(lambda: m.newStatus(2)) - - self.tray.setContextMenu(m) - self.tray.show() - self.tray.activated.connect(tray_activated) - - self.ms.show() - - updating = False - if settings['update'] and updater.updater_available() and updater.connection_available(): # auto update - version = updater.check_for_updates() - if version is not None: - if settings['update'] == 2: - updater.download(version) - updating = True - else: - reply = QtWidgets.QMessageBox.question(None, - 'Toxygen', - QtWidgets.QApplication.translate("login", - 'Update for Toxygen was found. Download and install it?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - updater.download(version) - updating = True - - if updating: - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - settings.close() - del self.tox - return - - plugin_helper = PluginLoader(self.tox, settings) # plugin support - plugin_helper.load() - - start() - # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) - self.init.start() - - # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) - self.mainloop.start() - self.avloop = self.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - if self.uri is not None: - self.ms.add_contact(self.uri) - - app.lastWindowClosed.connect(app.quit) - app.exec_() - - self.init.stop = True - self.mainloop.stop = True - self.avloop.stop = True - plugin_helper.stop() - stop() - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - self.tray.hide() - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - settings.close() - del self.tox - - def reset(self): - """ - Create new tox instance (new network settings) - :return: tox instance - """ - self.mainloop.stop = True - self.init.stop = True - self.avloop.stop = True - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - del self.tox - # create new tox instance - self.tox = profile.tox_factory(data, Settings.get_instance()) - # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) - self.init.start() - - # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) - self.mainloop.start() - - self.avloop = self.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - plugin_helper = PluginLoader.get_instance() - plugin_helper.set_tox(self.tox) - - return self.tox - - # ----------------------------------------------------------------------------------------------------------------- - # Inner classes - # ----------------------------------------------------------------------------------------------------------------- - - class InitThread(QtCore.QThread): - - def __init__(self, tox, ms, tray): - QtCore.QThread.__init__(self) - self.tox, self.ms, self.tray = tox, ms, tray - self.stop = False - - def run(self): - # initializing callbacks - init_callbacks(self.tox, self.ms, self.tray) - # download list of nodes if needed - download_nodes_list() - # bootstrap - try: - for data in generate_nodes(): - if self.stop: - return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) - except: - pass - for _ in range(10): - if self.stop: - return - self.msleep(1000) - while not self.tox.self_get_connection_status(): - try: - for data in generate_nodes(): - if self.stop: - return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) - except: - pass - finally: - self.msleep(5000) - - class ToxIterateThread(QtCore.QThread): - - def __init__(self, tox): - QtCore.QThread.__init__(self) - self.tox = tox - self.stop = False - - def run(self): - while not self.stop: - self.tox.iterate() - self.msleep(self.tox.iteration_interval()) - - class ToxAVIterateThread(QtCore.QThread): - - def __init__(self, toxav): - QtCore.QThread.__init__(self) - self.toxav = toxav - self.stop = False - - def run(self): - while not self.stop: - self.toxav.iterate() - self.msleep(self.toxav.iteration_interval()) class Login: @@ -462,7 +39,12 @@ def reset(): def main(): - if len(sys.argv) == 1: + parser = argparse.ArgumentParser() + parser.add_argument('--version') + parser.add_argument('--clean') + parser.add_argument('--reset') + args = parser.parse_args() + if not len(args): toxygen = Toxygen() else: # started with argument(s) arg = sys.argv[1] diff --git a/toxygen/messenger/__init__.py b/toxygen/messenger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/messages.py b/toxygen/messenger/messages.py similarity index 100% rename from toxygen/messages.py rename to toxygen/messenger/messages.py diff --git a/toxygen/network/__init__.py b/toxygen/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/tox_dns.py b/toxygen/network/tox_dns.py similarity index 98% rename from toxygen/tox_dns.py rename to toxygen/network/tox_dns.py index 26b9619..36702ee 100644 --- a/toxygen/tox_dns.py +++ b/toxygen/network/tox_dns.py @@ -1,7 +1,7 @@ import json import urllib.request from util import log -import settings +from user_data import settings from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/plugin_support/__init__.py b/toxygen/plugin_support/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/plugin_support.py b/toxygen/plugin_support/plugin_support.py similarity index 99% rename from toxygen/plugin_support.py rename to toxygen/plugin_support/plugin_support.py index 0ff7421..de39e5f 100644 --- a/toxygen/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -1,10 +1,10 @@ import util -import profile +from contacts import profile import os import importlib import inspect import plugins.plugin_super_class as pl -import toxes +from user_data import toxes import sys diff --git a/toxygen/smileys.py b/toxygen/smileys_and_stickers.py similarity index 100% rename from toxygen/smileys.py rename to toxygen/smileys_and_stickers.py diff --git a/toxygen/threads.py b/toxygen/threads.py new file mode 100644 index 0000000..dae8800 --- /dev/null +++ b/toxygen/threads.py @@ -0,0 +1,62 @@ +class InitThread(QtCore.QThread): + + def __init__(self, tox, ms, tray): + QtCore.QThread.__init__(self) + self.tox, self.ms, self.tray = tox, ms, tray + self.stop = False + + def run(self): + # initializing callbacks + init_callbacks(self.tox, self.ms, self.tray) + # download list of nodes if needed + download_nodes_list() + # bootstrap + try: + for data in generate_nodes(): + if self.stop: + return + self.tox.bootstrap(*data) + self.tox.add_tcp_relay(*data) + except: + pass + for _ in range(10): + if self.stop: + return + self.msleep(1000) + while not self.tox.self_get_connection_status(): + try: + for data in generate_nodes(): + if self.stop: + return + self.tox.bootstrap(*data) + self.tox.add_tcp_relay(*data) + except: + pass + finally: + self.msleep(5000) + + +class ToxIterateThread(QtCore.QThread): + + def __init__(self, tox): + QtCore.QThread.__init__(self) + self.tox = tox + self.stop = False + + def run(self): + while not self.stop: + self.tox.iterate() + self.msleep(self.tox.iteration_interval()) + + +class ToxAVIterateThread(QtCore.QThread): + + def __init__(self, toxav): + QtCore.QThread.__init__(self) + self.toxav = toxav + self.stop = False + + def run(self): + while not self.stop: + self.toxav.iterate() + self.msleep(self.toxav.iteration_interval()) diff --git a/toxygen/toxcore_enums_and_consts.py b/toxygen/toxcore_enums_and_consts.py deleted file mode 100644 index a17d93e..0000000 --- a/toxygen/toxcore_enums_and_consts.py +++ /dev/null @@ -1,220 +0,0 @@ -TOX_USER_STATUS = { - 'NONE': 0, - 'AWAY': 1, - 'BUSY': 2, -} - -TOX_MESSAGE_TYPE = { - 'NORMAL': 0, - 'ACTION': 1, -} - -TOX_PROXY_TYPE = { - 'NONE': 0, - 'HTTP': 1, - 'SOCKS5': 2, -} - -TOX_SAVEDATA_TYPE = { - 'NONE': 0, - 'TOX_SAVE': 1, - 'SECRET_KEY': 2, -} - -TOX_ERR_OPTIONS_NEW = { - 'OK': 0, - 'MALLOC': 1, -} - -TOX_ERR_NEW = { - 'OK': 0, - 'NULL': 1, - 'MALLOC': 2, - 'PORT_ALLOC': 3, - 'PROXY_BAD_TYPE': 4, - 'PROXY_BAD_HOST': 5, - 'PROXY_BAD_PORT': 6, - 'PROXY_NOT_FOUND': 7, - 'LOAD_ENCRYPTED': 8, - 'LOAD_BAD_FORMAT': 9, -} - -TOX_ERR_BOOTSTRAP = { - 'OK': 0, - 'NULL': 1, - 'BAD_HOST': 2, - 'BAD_PORT': 3, -} - -TOX_CONNECTION = { - 'NONE': 0, - 'TCP': 1, - 'UDP': 2, -} - -TOX_ERR_SET_INFO = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, -} - -TOX_ERR_FRIEND_ADD = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, - 'NO_MESSAGE': 3, - 'OWN_KEY': 4, - 'ALREADY_SENT': 5, - 'BAD_CHECKSUM': 6, - 'SET_NEW_NOSPAM': 7, - 'MALLOC': 8, -} - -TOX_ERR_FRIEND_DELETE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_BY_PUBLIC_KEY = { - 'OK': 0, - 'NULL': 1, - 'NOT_FOUND': 2, -} - -TOX_ERR_FRIEND_GET_PUBLIC_KEY = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_GET_LAST_ONLINE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_QUERY = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, -} - -TOX_ERR_SET_TYPING = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_SEND_MESSAGE = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'SENDQ': 4, - 'TOO_LONG': 5, - 'EMPTY': 6, -} - -TOX_FILE_KIND = { - 'DATA': 0, - 'AVATAR': 1, -} - -TOX_FILE_CONTROL = { - 'RESUME': 0, - 'PAUSE': 1, - 'CANCEL': 2, -} - -TOX_ERR_FILE_CONTROL = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'NOT_PAUSED': 4, - 'DENIED': 5, - 'ALREADY_PAUSED': 6, - 'SENDQ': 7, -} - -TOX_ERR_FILE_SEEK = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'DENIED': 4, - 'INVALID_POSITION': 5, - 'SENDQ': 6, -} - -TOX_ERR_FILE_GET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'NOT_FOUND': 3, -} - -TOX_ERR_FILE_SEND = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NAME_TOO_LONG': 4, - 'TOO_MANY': 5, -} - -TOX_ERR_FILE_SEND_CHUNK = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NOT_FOUND': 4, - 'NOT_TRANSFERRING': 5, - 'INVALID_LENGTH': 6, - 'SENDQ': 7, - 'WRONG_POSITION': 8, -} - -TOX_ERR_FRIEND_CUSTOM_PACKET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'INVALID': 4, - 'EMPTY': 5, - 'TOO_LONG': 6, - 'SENDQ': 7, -} - -TOX_ERR_GET_PORT = { - 'OK': 0, - 'NOT_BOUND': 1, -} - -TOX_CHAT_CHANGE = { - 'PEER_ADD': 0, - 'PEER_DEL': 1, - 'PEER_NAME': 2 -} - -TOX_GROUPCHAT_TYPE = { - 'TEXT': 0, - 'AV': 1 -} - -TOX_PUBLIC_KEY_SIZE = 32 - -TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 - -TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 - -TOX_MAX_MESSAGE_LENGTH = 1372 - -TOX_MAX_NAME_LENGTH = 128 - -TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 - -TOX_SECRET_KEY_SIZE = 32 - -TOX_FILE_ID_LENGTH = 32 - -TOX_HASH_LENGTH = 32 - -TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/tray.py b/toxygen/tray.py new file mode 100644 index 0000000..ffe9aeb --- /dev/null +++ b/toxygen/tray.py @@ -0,0 +1,83 @@ +self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + self.tray.setObjectName('tray') + + class Menu(QtWidgets.QMenu): + + def newStatus(self, status): + if not Settings.get_instance().locked: + profile.Profile.get_instance().set_status(status) + self.aboutToShowHandler() + self.hide() + + def aboutToShowHandler(self): + status = profile.Profile.get_instance().status + act = self.act + if status is None or Settings.get_instance().locked: + self.actions()[1].setVisible(False) + else: + self.actions()[1].setVisible(True) + act.actions()[0].setChecked(False) + act.actions()[1].setChecked(False) + act.actions()[2].setChecked(False) + act.actions()[status].setChecked(True) + self.actions()[2].setVisible(not Settings.get_instance().locked) + + def languageChange(self, *args, **kwargs): + self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) + self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) + self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) + self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) + self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away')) + self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy')) + + m = Menu() + show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) + sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) + onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) + away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) + busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) + onl.setCheckable(True) + away.setCheckable(True) + busy.setCheckable(True) + m.act = sub + exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) + + def show_window(): + s = Settings.get_instance() + + def show(): + if not self.ms.isActiveWindow(): + self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + self.ms.activateWindow() + self.ms.show() + if not s.locked: + show() + else: + def correct_pass(): + show() + s.locked = False + s.unlockScreen = False + if not s.unlockScreen: + s.unlockScreen = True + self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) + self.p.show() + + def tray_activated(reason): + if reason == QtWidgets.QSystemTrayIcon.DoubleClick: + show_window() + + def close_app(): + if not Settings.get_instance().locked: + settings.closing = True + self.ms.close() + + show.triggered.connect(show_window) + exit.triggered.connect(close_app) + m.aboutToShow.connect(lambda: m.aboutToShowHandler()) + onl.triggered.connect(lambda: m.newStatus(0)) + away.triggered.connect(lambda: m.newStatus(1)) + busy.triggered.connect(lambda: m.newStatus(2)) + + self.tray.setContextMenu(m) + self.tray.show() + self.tray.activated.connect(tray_activated) \ No newline at end of file diff --git a/toxygen/ui/__init__.py b/toxygen/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/avwidgets.py b/toxygen/ui/avwidgets.py similarity index 98% rename from toxygen/avwidgets.py rename to toxygen/ui/avwidgets.py index 8c81387..67bd04a 100644 --- a/toxygen/avwidgets.py +++ b/toxygen/ui/avwidgets.py @@ -1,10 +1,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets -import widgets -import profile +from ui import widgets +from contacts import profile import util import pyaudio import wave -import settings +from user_data import settings from util import curr_directory diff --git a/toxygen/items_factory.py b/toxygen/ui/items_factory.py similarity index 97% rename from toxygen/items_factory.py rename to toxygen/ui/items_factory.py index 44a00ad..cdf127a 100644 --- a/toxygen/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -1,5 +1,4 @@ -from PyQt5 import QtWidgets, QtCore -from list_items import * +from ui.list_items import * class ItemsFactory: diff --git a/toxygen/list_items.py b/toxygen/ui/list_items.py similarity index 99% rename from toxygen/list_items.py rename to toxygen/ui/list_items.py index 9b92f2a..0e4991e 100644 --- a/toxygen/list_items.py +++ b/toxygen/ui/list_items.py @@ -1,12 +1,12 @@ -from toxcore_enums_and_consts import * +from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets -import profile +from contacts import profile from file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from util import curr_directory, convert_time, curr_time -from widgets import DataLabel, create_menu +from ui.widgets import DataLabel, create_menu import html as h import smileys -import settings +from user_data import settings import re @@ -60,7 +60,7 @@ class MessageEdit(QtWidgets.QTextBrowser): def quote_text(self): text = self.textCursor().selection().toPlainText() if text: - import mainscreen + from ui import mainscreen window = mainscreen.MainWindow.get_instance() text = '>' + '\n>'.join(text.split('\n')) if window.messageEdit.toPlainText(): @@ -70,7 +70,7 @@ class MessageEdit(QtWidgets.QTextBrowser): def on_anchor_clicked(self, url): text = str(url.toString()) if text.startswith('tox:'): - import menu + from ui import menu self.add_contact = menu.AddContact(text[4:]) self.add_contact.show() else: diff --git a/toxygen/loginscreen.py b/toxygen/ui/loginscreen.py similarity index 98% rename from toxygen/loginscreen.py rename to toxygen/ui/loginscreen.py index 77aa5ba..cc1f45c 100644 --- a/toxygen/loginscreen.py +++ b/toxygen/ui/loginscreen.py @@ -1,5 +1,4 @@ -from PyQt5 import QtWidgets, QtCore -from widgets import * +from ui.widgets import * class NickEdit(LineEdit): diff --git a/toxygen/mainscreen.py b/toxygen/ui/mainscreen.py similarity index 99% rename from toxygen/mainscreen.py rename to toxygen/ui/mainscreen.py index c76f19b..ce828b0 100644 --- a/toxygen/mainscreen.py +++ b/toxygen/ui/mainscreen.py @@ -1,11 +1,10 @@ -from menu import * -from profile import * -from list_items import * -from widgets import MultilineEdit, ComboBox +from ui.menu import * +from contacts.profile import * +from ui.list_items import * +from ui.widgets import MultilineEdit, ComboBox import plugin_support -from mainscreen_widgets import * -import settings -import toxes +from ui.mainscreen_widgets import * +from user_data import toxes, settings class MainWindow(QtWidgets.QMainWindow, Singleton): diff --git a/toxygen/mainscreen_widgets.py b/toxygen/ui/mainscreen_widgets.py similarity index 99% rename from toxygen/mainscreen_widgets.py rename to toxygen/ui/mainscreen_widgets.py index 9955771..bbf6c5a 100644 --- a/toxygen/mainscreen_widgets.py +++ b/toxygen/ui/mainscreen_widgets.py @@ -1,6 +1,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets -from widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit -from profile import Profile +from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit +from contacts.profile import Profile import smileys import util @@ -333,7 +333,7 @@ class WelcomeScreen(CenteredWidget): QtCore.QTimer.singleShot(1000, self.show) def not_show(self): - import settings + from user_data import settings s = settings.Settings.get_instance() s['show_welcome_screen'] = False s.save() diff --git a/toxygen/menu.py b/toxygen/ui/menu.py similarity index 99% rename from toxygen/menu.py rename to toxygen/ui/menu.py index 17f4e17..606c545 100644 --- a/toxygen/menu.py +++ b/toxygen/ui/menu.py @@ -1,10 +1,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets -from settings import * -from profile import Profile +from user_data.settings import * +from contacts.profile import Profile from util import curr_directory, copy -from widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow +from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio -import toxes +from user_data import toxes import plugin_support import updater @@ -160,7 +160,7 @@ class ProfileSettings(CenteredWidget): self.default = QtWidgets.QPushButton(self) self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) path, name = Settings.get_auto_profile() - self.auto = path + name == ProfileHelper.get_path() + Settings.get_instance().name + self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name self.default.clicked.connect(self.auto_profile) self.retranslateUi() if profile.status is not None: @@ -199,7 +199,7 @@ class ProfileSettings(CenteredWidget): if self.auto: Settings.reset_auto_profile() else: - Settings.set_auto_profile(ProfileHelper.get_path(), Settings.get_instance().name) + Settings.set_auto_profile(ProfileManager.get_path(), Settings.get_instance().name) self.auto = not self.auto if self.auto: self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as not default profile")) @@ -275,7 +275,7 @@ class ProfileSettings(CenteredWidget): settings.export(directory) profile = Profile.get_instance() profile.export_db(directory) - ProfileHelper.get_instance().export_profile(directory, reply == QtWidgets.QMessageBox.Yes) + ProfileManager.get_instance().export_profile(directory, reply == QtWidgets.QMessageBox.Yes) def closeEvent(self, event): profile = Profile.get_instance() diff --git a/toxygen/passwordscreen.py b/toxygen/ui/passwordscreen.py similarity index 99% rename from toxygen/passwordscreen.py rename to toxygen/ui/passwordscreen.py index ca721e5..9d3d523 100644 --- a/toxygen/passwordscreen.py +++ b/toxygen/ui/passwordscreen.py @@ -1,4 +1,4 @@ -from widgets import CenteredWidget, LineEdit +from ui.widgets import CenteredWidget, LineEdit from PyQt5 import QtCore, QtWidgets diff --git a/toxygen/widgets.py b/toxygen/ui/widgets.py similarity index 100% rename from toxygen/widgets.py rename to toxygen/ui/widgets.py diff --git a/toxygen/updater/__init__.py b/toxygen/updater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/updater.py b/toxygen/updater/updater.py similarity index 99% rename from toxygen/updater.py rename to toxygen/updater/updater.py index 762892a..af80624 100644 --- a/toxygen/updater.py +++ b/toxygen/updater/updater.py @@ -1,6 +1,6 @@ import util import os -import settings +from user_data import settings import platform import urllib from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/user_data/__init__.py b/toxygen/user_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py new file mode 100644 index 0000000..96f591c --- /dev/null +++ b/toxygen/user_data/profile_manager.py @@ -0,0 +1,70 @@ +class ProfileManager(): + """ + Class with methods for search, load and save profiles + """ + def __init__(self, path, name): + path = append_slash(path) + self._path = path + name + '.tox' + self._directory = path + # create /avatars if not exists: + directory = path + 'avatars' + if not os.path.exists(directory): + os.makedirs(directory) + + def open_profile(self): + with open(self._path, 'rb') as fl: + data = fl.read() + if data: + return data + else: + raise IOError('Save file has zero size!') + + def get_dir(self): + return self._directory + + def save_profile(self, data): + inst = ToxES.get_instance() + if inst.has_password(): + data = inst.pass_encrypt(data) + with open(self._path, 'wb') as fl: + fl.write(data) + print('Profile saved successfully') + + def export_profile(self, new_path, use_new_path): + path = new_path + os.path.basename(self._path) + with open(self._path, 'rb') as fin: + data = fin.read() + with open(path, 'wb') as fout: + fout.write(data) + print('Profile exported successfully') + copy(self._directory + 'avatars', new_path + 'avatars') + if use_new_path: + self._path = new_path + os.path.basename(self._path) + self._directory = new_path + Settings.get_instance().update_path() + + @staticmethod + def find_profiles(): + """ + Find available tox profiles + """ + path = Settings.get_default_path() + result = [] + # check default path + if not os.path.exists(path): + os.makedirs(path) + for fl in os.listdir(path): + if fl.endswith('.tox'): + name = fl[:-4] + result.append((path, name)) + path = curr_directory() + # check current directory + for fl in os.listdir(path): + if fl.endswith('.tox'): + name = fl[:-4] + result.append((path + '/', name)) + return result + + @staticmethod + def get_path(): + return ProfileManager.get_instance().get_dir() diff --git a/toxygen/settings.py b/toxygen/user_data/settings.py similarity index 72% rename from toxygen/settings.py rename to toxygen/user_data/settings.py index 431688a..45d41bd 100644 --- a/toxygen/settings.py +++ b/toxygen/user_data/settings.py @@ -3,7 +3,7 @@ import json import os from util import Singleton, curr_directory, log, copy, append_slash import pyaudio -from toxes import ToxES +from user_data.toxes import ToxES import smileys @@ -14,7 +14,7 @@ class Settings(dict, Singleton): def __init__(self, name): Singleton.__init__(self) - self.path = ProfileHelper.get_path() + str(name) + '.json' + self.path = ProfileManager.get_path() + str(name) + '.json' self.name = name if os.path.isfile(self.path): with open(self.path, 'rb') as fl: @@ -184,7 +184,7 @@ class Settings(dict, Singleton): fl.write(text) def close(self): - profile_path = ProfileHelper.get_path() + profile_path = ProfileManager.get_path() path = str(profile_path + str(self.name) + '.lock') if os.path.isfile(path): os.remove(path) @@ -193,7 +193,7 @@ class Settings(dict, Singleton): """ Mark current profile as active """ - profile_path = ProfileHelper.get_path() + profile_path = ProfileManager.get_path() path = str(profile_path + str(self.name) + '.lock') with open(path, 'w') as fl: fl.write('active') @@ -204,7 +204,7 @@ class Settings(dict, Singleton): fl.write(text) def update_path(self): - self.path = ProfileHelper.get_path() + self.name + '.json' + self.path = ProfileManager.get_path() + self.name + '.json' @staticmethod def get_global_settings_path(): @@ -219,75 +219,3 @@ class Settings(dict, Singleton): else: return os.getenv('HOME') + '/.config/tox/' - -class ProfileHelper(Singleton): - """ - Class with methods for search, load and save profiles - """ - def __init__(self, path, name): - Singleton.__init__(self) - path = append_slash(path) - self._path = path + name + '.tox' - self._directory = path - # create /avatars if not exists: - directory = path + 'avatars' - if not os.path.exists(directory): - os.makedirs(directory) - - def open_profile(self): - with open(self._path, 'rb') as fl: - data = fl.read() - if data: - return data - else: - raise IOError('Save file has zero size!') - - def get_dir(self): - return self._directory - - def save_profile(self, data): - inst = ToxES.get_instance() - if inst.has_password(): - data = inst.pass_encrypt(data) - with open(self._path, 'wb') as fl: - fl.write(data) - print('Profile saved successfully') - - def export_profile(self, new_path, use_new_path): - path = new_path + os.path.basename(self._path) - with open(self._path, 'rb') as fin: - data = fin.read() - with open(path, 'wb') as fout: - fout.write(data) - print('Profile exported successfully') - copy(self._directory + 'avatars', new_path + 'avatars') - if use_new_path: - self._path = new_path + os.path.basename(self._path) - self._directory = new_path - Settings.get_instance().update_path() - - @staticmethod - def find_profiles(): - """ - Find available tox profiles - """ - path = Settings.get_default_path() - result = [] - # check default path - if not os.path.exists(path): - os.makedirs(path) - for fl in os.listdir(path): - if fl.endswith('.tox'): - name = fl[:-4] - result.append((path, name)) - path = curr_directory() - # check current directory - for fl in os.listdir(path): - if fl.endswith('.tox'): - name = fl[:-4] - result.append((path + '/', name)) - return result - - @staticmethod - def get_path(): - return ProfileHelper.get_instance().get_dir() diff --git a/toxygen/toxes.py b/toxygen/user_data/toxes.py similarity index 77% rename from toxygen/toxes.py rename to toxygen/user_data/toxes.py index 5b7282f..5a22f61 100644 --- a/toxygen/toxes.py +++ b/toxygen/user_data/toxes.py @@ -1,12 +1,8 @@ -import util -import toxencryptsave +class ToxES: -class ToxES(util.Singleton): - - def __init__(self): - super().__init__() - self._toxencryptsave = toxencryptsave.ToxEncryptSave() + def __init__(self, toxencryptsave): + self._toxencryptsave = toxencryptsave self._passphrase = None def set_password(self, passphrase): diff --git a/toxygen/util/__init__.py b/toxygen/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/util.py b/toxygen/util/util.py similarity index 98% rename from toxygen/util.py rename to toxygen/util/util.py index e8d702a..bb27da6 100644 --- a/toxygen/util.py +++ b/toxygen/util/util.py @@ -5,7 +5,7 @@ import sys import re -program_version = '0.4.1' +program_version = '0.5.0' def cached(func): diff --git a/toxygen/wrapper/__init__.py b/toxygen/wrapper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/libtox.py b/toxygen/wrapper/libtox.py similarity index 100% rename from toxygen/libtox.py rename to toxygen/wrapper/libtox.py diff --git a/toxygen/tox.py b/toxygen/wrapper/tox.py similarity index 68% rename from toxygen/tox.py rename to toxygen/wrapper/tox.py index ef4e44c..c6f5912 100644 --- a/toxygen/tox.py +++ b/toxygen/wrapper/tox.py @@ -1,7 +1,7 @@ from ctypes import * -from toxcore_enums_and_consts import * -from toxav import ToxAV -from libtox import LibToxCore +from wrapper.toxcore_enums_and_consts import * +from wrapper.toxav import ToxAV +from wrapper.libtox import LibToxCore class ToxOptions(Structure): @@ -1514,88 +1514,842 @@ class Tox: elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: raise RuntimeError('The instance was not bound to any port.') - # ----------------------------------------------------------------------------------------------------------------- - # Group chats +# ----------------------------------------------------------------------------------------------------------------- + # Group chat instance management # ----------------------------------------------------------------------------------------------------------------- - def del_groupchat(self, groupnumber): - result = Tox.libtoxcore.tox_del_groupchat(self._tox_pointer, c_int(groupnumber), None) + def group_new(self, privacy_state, group_name): + """ + Creates a new group chat. + This function creates a new group chat object and adds it to the chats array. + The client should initiate its peer list with self info after calling this function, as + the peer_join callback will not be triggered. + :param privacy_state: The privacy state of the group. If this is set to TOX_GROUP_PRIVACY_STATE_PUBLIC, + the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. + Otherwise a friend invite will be required to join the group. + :param group_name: The name of the group. The name must be non-NULL. + :return group number on success, UINT32_MAX on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name, + len(group_name), byref(error)) return result - def group_peername(self, groupnumber, peernumber): - buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_peername(self._tox_pointer, c_int(groupnumber), c_int(peernumber), - buffer, None) - return str(buffer[:result], 'utf-8') + def group_join(self, chat_id, password): + """ + Joins a group chat with specified Chat ID. + This function creates a new group chat object, adds it to the chats array, and sends + a DHT announcement to find peers in the group associated with chat_id. Once a peer has been + found a join attempt will be initiated. + :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. + :param password: The password required to join the group. Set to NULL if no password is required. + :return groupnumber on success, UINT32_MAX on failure. + """ - def invite_friend(self, friendnumber, groupnumber): - result = Tox.libtoxcore.tox_invite_friend(self._tox_pointer, c_int(friendnumber), - c_int(groupnumber), None) + error = c_int() + result = Tox.libtoxcore.tox_group_join(self._tox_pointer, string_to_bin(chat_id), + password, + len(password) if password is not None else 0, + byref(error)) return result - def join_groupchat(self, friendnumber, data): - result = Tox.libtoxcore.tox_join_groupchat(self._tox_pointer, - c_int(friendnumber), c_char_p(data), c_uint16(len(data)), None) + def group_reconnect(self, groupnumber): + """ + Reconnects to a group. + This function disconnects from all peers in the group, then attempts to reconnect with the group. + The caller's state is not changed (i.e. name, status, role, chat public key etc.) + :param groupnumber: The group number of the group we wish to reconnect to. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, groupnumber, byref(error)) return result - def group_message_send(self, groupnumber, message): - result = Tox.libtoxcore.tox_group_message_send(self._tox_pointer, c_int(groupnumber), c_char_p(message), - c_uint16(len(message)), None) + def group_leave(self, groupnumber, message): + """ + Leaves a group. + This function sends a parting packet containing a custom (non-obligatory) message to all + peers in a group, and deletes the group from the chat array. All group state information is permanently + lost, including keys and role credentials. + :param groupnumber: The group number of the group we wish to leave. + :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to + send a parting message. + :return True if the group chat instance was successfully deleted. + """ + + error = c_int() + f = Tox.libtoxcore.tox_group_leave + f.restype = c_bool + result = f(self._tox_pointer, groupnumber, message, + len(message) if message is not None else 0, byref(error)) return result - def group_action_send(self, groupnumber, action): - result = Tox.libtoxcore.tox_group_action_send(self._tox_pointer, - c_int(groupnumber), c_char_p(action), - c_uint16(len(action)), None) + # ----------------------------------------------------------------------------------------------------------------- + # Group user-visible client information (nickname/status/role/public key) + # ----------------------------------------------------------------------------------------------------------------- + + def group_self_set_name(self, groupnumber, name): + """ + Set the client's nickname for the group instance designated by the given group number. + Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is equal to zero or name is a NULL + pointer, the function call will fail. + :param name: A byte array containing the new nickname. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, groupnumber, name, len(name), byref(error)) return result - def group_set_title(self, groupnumber, title): - result = Tox.libtoxcore.tox_group_set_title(self._tox_pointer, c_int(groupnumber), - c_char_p(title), c_uint8(len(title)), None) + def group_self_get_name_size(self, groupnumber): + """ + Return the length of the client's current nickname for the group instance designated + by groupnumber as passed to tox_group_self_set_name. + If no nickname was set before calling this function, the name is empty, + and this function returns 0. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, groupnumber, byref(error)) return result - def group_get_title(self, groupnumber): - buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_get_title(self._tox_pointer, - c_int(groupnumber), buffer, - c_uint32(TOX_MAX_NAME_LENGTH), None) - return str(buffer[:result], 'utf-8') + def group_self_get_name(self, groupnumber): + """ + Write the nickname set by tox_group_self_set_name to a byte array. + If no nickname was set before calling this function, the name is empty, + and this function has no effect. + Call tox_group_self_get_name_size to find out how much memory to allocate for the result. + :return nickname + """ - def group_number_peers(self, groupnumber): - result = Tox.libtoxcore.tox_group_number_peers(self._tox_pointer, c_int(groupnumber), None) + error = c_int() + size = self.group_self_get_name_size(groupnumber) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, groupnumber, name, byref(error)) + return str(name[:size], 'utf-8') + + def group_self_set_status(self, groupnumber, status): + + """ + Set the client's status for the group instance. Status must be a TOX_USER_STATUS. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, groupnumber, status, byref(error)) return result - def add_av_groupchat(self): - result = self.AV.libtoxav.toxav_add_av_groupchat(self._tox_pointer, None, None) + def group_self_get_status(self, groupnumber): + """ + returns the client's status for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, groupnumber, byref(error)) return result - def join_av_groupchat(self, friendnumber, data): - result = self.AV.libtoxav.toxav_join_av_groupchat(self._tox_pointer, c_int32(friendnumber), - c_char_p(data), c_uint16(len(data)), - None, None) + def group_self_get_role(self, groupnumber): + """ + returns the client's role for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, groupnumber, byref(error)) return result - def callback_group_invite(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int32, c_uint8, POINTER(c_uint8), c_uint16, c_void_p) - self.group_invite_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) + def group_self_get_peer_id(self, groupnumber): + """ + returns the client's peer id for the group instance on success. + return value is unspecified on failure. + """ - def callback_group_message(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint16, c_void_p) + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_self_get_public_key(self, groupnumber): + """ + Write the client's group public key designated by the given group number to a byte array. + This key will be permanently tied to the client's identity for this particular group until + the client explicitly leaves the group or gets kicked/banned. This key is the only way for + other peers to reliably identify the client across client restarts. + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + :return public key + """ + + error = c_int() + key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, groupnumber, + key, byref(error)) + return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + + # ----------------------------------------------------------------------------------------------------------------- + # Peer-specific group state queries. + # ----------------------------------------------------------------------------------------------------------------- + + def group_peer_get_name_size(self, groupnumber, peer_id): + """ + Return the length of the peer's name. If the group number or ID is invalid, the + return value is unspecified. + The return value is equal to the `length` argument received by the last + `group_peer_name` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, groupnumber, peer_id, byref(error)) + return result + + def group_peer_get_name(self, groupnumber, peer_id): + """ + Write the name of the peer designated by the given ID to a byte + array. + Call tox_group_peer_get_name_size to determine the allocation size for the `name` parameter. + The data written to `name` is equal to the data received by the last + `group_peer_name` callback. + :param groupnumber: The group number of the group we wish to query. + :param peer_id: The ID of the peer whose name we want to retrieve. + :return name. + """ + error = c_int() + size = self.group_peer_get_name_size(groupnumber, peer_id) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, groupnumber, peer_id, name, byref(error)) + return str(name[:], 'utf-8') + + def group_peer_get_status(self, groupnumber, peer_id): + """ + Return the peer's user status (away/busy/...). If the ID or group number is + invalid, the return value is unspecified. + The status returned is equal to the last status received through the + `group_peer_status` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, groupnumber, peer_id, byref(error)) + return result + + def group_peer_get_role(self, groupnumber, peer_id): + """ + Return the peer's role (user/moderator/founder...). If the ID or group number is + invalid, the return value is unspecified. + The role returned is equal to the last role received through the + `group_moderation` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, groupnumber, peer_id, byref(error)) + return result + + def group_peer_get_public_key(self, groupnumber, peer_id): + """ + Write the group public key with the designated peer_id for the designated group number to public_key. + This key will be parmanently tied to a particular peer until they explicitly leave the group or + get kicked/banned, and is the only way to reliably identify the same peer across client restarts. + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + :return public key + """ + + error = c_int() + key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, groupnumber, peer_id, + key, byref(error)) + return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + + def callback_group_peer_name(self, callback, user_data): + """ + Set the callback for the `group_peer_name` event. Pass NULL to unset. + This event is triggered when a peer changes their nickname. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_peer_name_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_name(self._tox_pointer, self.group_peer_name_cb, user_data) + + def callback_group_peer_status(self, callback, user_data): + """ + Set the callback for the `group_peer_status` event. Pass NULL to unset. + This event is triggered when a peer changes their status. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) + self.group_peer_status_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_status(self._tox_pointer, self.group_peer_status_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat state queries and events. + # ----------------------------------------------------------------------------------------------------------------- + + def group_set_topic(self, groupnumber, topic): + """ + Set the group topic and broadcast it to the rest of the group. + topic length cannot be longer than TOX_GROUP_MAX_TOPIC_LENGTH. If length is equal to zero or + topic is set to NULL, the topic will be unset. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, groupnumber, topic, len(topic), byref(error)) + return result + + def group_get_topic_size(self, groupnumber): + """ + Return the length of the group topic. If the group number is invalid, the + return value is unspecified. + The return value is equal to the `length` argument received by the last + `group_topic` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_topic(self, groupnumber): + """ + Write the topic designated by the given group number to a byte array. + Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter. + The data written to `topic` is equal to the data received by the last + `group_topic` callback. + :return topic + """ + + error = c_int() + size = self.group_get_topic_size(groupnumber) + topic = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, groupnumber, topic, byref(error)) + return str(topic[:size], 'utf-8') + + def group_get_name_size(self, groupnumber): + """ + Return the length of the group name. If the group number is invalid, the + return value is unspecified. + """ + error = c_int() + result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_name(self, groupnumber): + """ + Write the name of the group designated by the given group number to a byte array. + Call tox_group_get_name_size to determine the allocation size for the `name` parameter. + :return true on success. + """ + + error = c_int() + size = self.group_get_name_size(groupnumber) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, groupnumber, + name, byref(error)) + return str(name[:size], 'utf-8') + + def group_get_chat_id(self, groupnumber): + """ + Write the Chat ID designated by the given group number to a byte array. + `chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes. + :return chat id. + """ + + error = c_int() + buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE) + result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, groupnumber, + buff, byref(error)) + return bin_to_string(buff, TOX_GROUP_CHAT_ID_SIZE) + + def group_get_number_groups(self): + """ + Return the number of groups in the Tox chats array. + """ + + result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer) + return result + + def group_get_privacy_state(self, groupnumber): + """ + Return the privacy state of the group designated by the given group number. If group number + is invalid, the return value is unspecified. + The value returned is equal to the data received by the last + `group_privacy_state` callback. + see the `Group chat founder controls` section for the respective set function. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_peer_limit(self, groupnumber): + """ + Return the maximum number of peers allowed for the group designated by the given group number. + If the group number is invalid, the return value is unspecified. + The value returned is equal to the data received by the last + `group_peer_limit` callback. + see the `Group chat founder controls` section for the respective set function. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_password_size(self, groupnumber): + """ + Return the length of the group password. If the group number is invalid, the + return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_password(self, groupnumber): + """ + Write the password for the group designated by the given group number to a byte array. + Call tox_group_get_password_size to determine the allocation size for the `password` parameter. + The data received is equal to the data received by the last + `group_password` callback. + see the `Group chat founder controls` section for the respective set function. + :return password + """ + + error = c_int() + size = self.group_get_password_size(groupnumber) + password = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, groupnumber, + password, byref(error)) + return str(password[:size], 'utf-8') + + def callback_group_topic(self, callback, user_data): + """ + Set the callback for the `group_topic` event. Pass NULL to unset. + This event is triggered when a peer changes the group topic. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_topic_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_topic(self._tox_pointer, self.group_topic_cb, user_data) + + def callback_group_privacy_state(self, callback, user_data): + """ + Set the callback for the `group_privacy_state` event. Pass NULL to unset. + This event is triggered when the group founder changes the privacy state. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.group_privacy_state_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_privacy_state(self._tox_pointer, self.group_privacy_state_cb, user_data) + + def callback_group_peer_limit(self, callback, user_data): + """ + Set the callback for the `group_peer_limit` event. Pass NULL to unset. + This event is triggered when the group founder changes the maximum peer limit. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.group_peer_limit_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_limit(self._tox_pointer, self.group_peer_limit_cb, user_data) + + def callback_group_password(self, callback, user_data): + """ + Set the callback for the `group_password` event. Pass NULL to unset. + This event is triggered when the group founder changes the group password. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_password_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_password(self._tox_pointer, self.group_password_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group message sending + # ----------------------------------------------------------------------------------------------------------------- + + def group_send_custom_packet(self, groupnumber, lossless, data): + """ + Send a custom packet to the group. + If lossless is true the packet will be lossless. Lossless packet behaviour is comparable + to TCP (reliability, arrive in order) but with packets instead of a stream. + If lossless is false, the packet will be lossy. Lossy packets behave like UDP packets, + meaning they might never reach the other side or might arrive more than once (if someone + is messing with the connection) or might arrive in the wrong order. + Unless latency is an issue or message reliability is not important, it is recommended that you use + lossless custom packets. + :param groupnumber: The group number of the group the message is intended for. + :param lossless: True if the packet should be lossless. + :param data A byte array containing the packet data. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, groupnumber, lossless, data, + len(data), byref(error)) + return result + + def group_send_private_message(self, groupnumber, peer_id, message): + """ + Send a text chat message to the specified peer in the specified group. + This function creates a group private message packet and pushes it into the send + queue. + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages + must be split by the client and sent as separate messages. Other clients can + then reassemble the fragments. Messages may not be empty. + :param groupnumber: The group number of the group the message is intended for. + :param peer_id: The ID of the peer the message is intended for. + :param message: A non-NULL pointer to the first element of a byte array containing the message text. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, groupnumber, peer_id, message, + len(message), byref(error)) + return result + + def group_send_message(self, groupnumber, type, message): + """ + Send a text chat message to the group. + This function creates a group message packet and pushes it into the send + queue. + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages + must be split by the client and sent as separate messages. Other clients can + then reassemble the fragments. Messages may not be empty. + :param groupnumber: The group number of the group the message is intended for. + :param type: Message type (normal, action, ...). + :param message: A non-NULL pointer to the first element of a byte array containing the message text. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, groupnumber, type, message, len(message), + byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group message receiving + # ----------------------------------------------------------------------------------------------------------------- + + def callback_group_message(self, callback, user_data): + """ + Set the callback for the `group_message` event. Pass NULL to unset. + This event is triggered when the client receives a group message. + Callback: python function with params: + tox Tox* instance + groupnumber The group number of the group the message is intended for. + peer_id The ID of the peer who sent the message. + type The type of message (normal, action, ...). + message The message data. + length The length of the message. + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_char_p, c_size_t, c_void_p) self.group_message_cb = c_callback(callback) Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, self.group_message_cb, user_data) - def callback_group_action(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint16, c_void_p) - self.group_action_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_action(self._tox_pointer, self.group_action_cb, user_data) + def callback_group_private_message(self, callback, user_data): + """ + Set the callback for the `group_private_message` event. Pass NULL to unset. + This event is triggered when the client receives a private message. + """ - def callback_group_title(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint8, c_void_p) - self.group_title_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_title(self._tox_pointer, self.group_title_cb, user_data) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_private_message_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_private_message(self._tox_pointer, self.group_private_message_cb, user_data) - def callback_group_namelist_change(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_uint8, c_void_p) - self.group_namelist_change_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_namelist_change(self._tox_pointer, self.group_namelist_change_cb, user_data) + def callback_group_custom_packet(self, callback, user_data): + """ + Set the callback for the `group_custom_packet` event. Pass NULL to unset. + This event is triggered when the client receives a custom packet. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, POINTER(c_uint8), c_void_p) + self.group_custom_packet_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_custom_packet(self._tox_pointer, self.group_custom_packet_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat inviting and join/part events + # ----------------------------------------------------------------------------------------------------------------- + + def group_invite_friend(self, groupnumber, friend_number): + """ + Invite a friend to a group. + This function creates an invite request packet and pushes it to the send queue. + :param groupnumber: The group number of the group the message is intended for. + :param friend_number: The friend number of the friend the invite is intended for. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, groupnumber, friend_number, byref(error)) + return result + + def group_invite_accept(self, invite_data, friend_number, password=None): + """ + Accept an invite to a group chat that the client previously received from a friend. The invite + is only valid while the inviter is present in the group. + :param invite_data: The invite data received from the `group_invite` event. + :param password: The password required to join the group. Set to NULL if no password is required. + :return the groupnumber on success, UINT32_MAX on failure. + """ + + error = c_int() + f = Tox.libtoxcore.tox_group_invite_accept + f.restype = c_uint32 + result = f(self._tox_pointer, friend_number, invite_data, len(invite_data), password, + len(password) if password is not None else 0, byref(error)) + print('Invite accept. Result:', result, 'Error:', error.value) + return result + + def callback_group_invite(self, callback, user_data): + """ + Set the callback for the `group_invite` event. Pass NULL to unset. + This event is triggered when the client receives a group invite from a friend. The client must store + invite_data which is used to join the group via tox_group_invite_accept. + Callback: python function with params: + tox - Tox* + friend_number The friend number of the contact who sent the invite. + invite_data The invite data. + length The length of invite_data. + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) + self.group_invite_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) + + def callback_group_peer_join(self, callback, user_data): + """ + Set the callback for the `group_peer_join` event. Pass NULL to unset. + This event is triggered when a peer other than self joins the group. + Callback: python function with params: + tox - Tox* + group_number - group number + peer_id - peer id + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.group_peer_join_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_join(self._tox_pointer, self.group_peer_join_cb, user_data) + + def callback_group_peer_exit(self, callback, user_data): + """ + Set the callback for the `group_peer_exit` event. Pass NULL to unset. + This event is triggered when a peer other than self exits the group. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_peer_exit_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_exit(self._tox_pointer, self.group_peer_exit_cb, user_data) + + def callback_group_self_join(self, callback, user_data): + """ + Set the callback for the `group_self_join` event. Pass NULL to unset. + This event is triggered when the client has successfully joined a group. Use this to initialize + any group information the client may need. + Callback: python fucntion with params: + tox - *Tox + group_number - group number + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p) + self.group_self_join_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_self_join(self._tox_pointer, self.group_self_join_cb, user_data) + + def callback_group_join_fail(self, callback, user_data): + """ + Set the callback for the `group_join_fail` event. Pass NULL to unset. + This event is triggered when the client fails to join a group. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.group_join_fail_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_join_fail(self._tox_pointer, self.group_join_fail_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat founder controls (these only work for the group founder) + # ----------------------------------------------------------------------------------------------------------------- + + def group_founder_set_password(self, groupnumber, password): + """ + Set or unset the group password. + This function sets the groups password, creates a new group shared state including the change, + and distributes it to the rest of the group. + :param groupnumber: The group number of the group for which we wish to set the password. + :param password: The password we want to set. Set password to NULL to unset the password. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, groupnumber, password, + len(password), byref(error)) + return result + + def group_founder_set_privacy_state(self, groupnumber, privacy_state): + """ + Set the group privacy state. + This function sets the group's privacy state, creates a new group shared state + including the change, and distributes it to the rest of the group. + If an attempt is made to set the privacy state to the same state that the group is already + in, the function call will be successful and no action will be taken. + :param groupnumber: The group number of the group for which we wish to change the privacy state. + :param privacy_state: The privacy state we wish to set the group to. + :return true on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, groupnumber, privacy_state, + byref(error)) + return result + + def group_founder_set_peer_limit(self, groupnumber, max_peers): + """ + Set the group peer limit. + This function sets a limit for the number of peers who may be in the group, creates a new + group shared state including the change, and distributes it to the rest of the group. + :param groupnumber: The group number of the group for which we wish to set the peer limit. + :param max_peers: The maximum number of peers to allow in the group. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, groupnumber, + max_peers, byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat moderation + # ----------------------------------------------------------------------------------------------------------------- + + def group_toggle_ignore(self, groupnumber, peer_id, ignore): + """ + Ignore or unignore a peer. + :param groupnumber: The group number of the group the in which you wish to ignore a peer. + :param peer_id: The ID of the peer who shall be ignored or unignored. + :param ignore: True to ignore the peer, false to unignore the peer. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_toggle_ignore(self._tox_pointer, groupnumber, peer_id, ignore, byref(error)) + return result + + def group_mod_set_role(self, groupnumber, peer_id, role): + """ + Set a peer's role. + This function will first remove the peer's previous role and then assign them a new role. + It will also send a packet to the rest of the group, requesting that they perform + the role reassignment. Note: peers cannot be set to the founder role. + :param groupnumber: The group number of the group the in which you wish set the peer's role. + :param peer_id: The ID of the peer whose role you wish to set. + :param role: The role you wish to set the peer to. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, groupnumber, peer_id, role, byref(error)) + return result + + def group_mod_remove_peer(self, groupnumber, peer_id, set_ban): + """ + Kick/ban a peer. + This function will remove a peer from the caller's peer list and optionally add their IP address + to the ban list. It will also send a packet to all group members requesting them + to do the same. + :param groupnumber: The group number of the group the ban is intended for. + :param peer_id: The ID of the peer who will be kicked and/or added to the ban list. + :param set_ban: Set to true if a ban shall be set on the peer's IP address. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, groupnumber, peer_id, + set_ban, byref(error)) + return result + + def group_mod_remove_ban(self, groupnumber, ban_id): + """ + Removes a ban. + This function removes a ban entry from the ban list, and sends a packet to the rest of + the group requesting that they do the same. + :param groupnumber: The group number of the group in which the ban is to be removed. + :param ban_id: The ID of the ban entry that shall be removed. + :return True on success + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_remove_ban(self._tox_pointer, groupnumber, ban_id, byref(error)) + return result + + def callback_group_moderation(self, callback, user_data): + """ + Set the callback for the `group_moderation` event. Pass NULL to unset. + This event is triggered when a moderator or founder executes a moderation event. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_int, c_void_p) + self.group_moderation_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_moderation(self._tox_pointer, self.group_moderation_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat ban list queries + # ----------------------------------------------------------------------------------------------------------------- + + def group_ban_get_list_size(self, groupnumber): + """ + Return the number of entries in the ban list for the group designated by + the given group number. If the group number is invalid, the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_list_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_ban_get_list(self, groupnumber): + """ + Copy a list of valid ban list ID's into an array. + Call tox_group_ban_get_list_size to determine the number of elements to allocate. + return true on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, groupnumber, POINTER(c_uint32)( + create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(groupnumber)), byref(error))) + return result + + def group_ban_get_name_size(self, groupnumber, ban_id): + """ + Return the length of the name for the ban list entry designated by ban_id, in the + group designated by the given group number. If either groupnumber or ban_id is invalid, + the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_name_size(self._tox_pointer, groupnumber, ban_id, byref(error)) + return result + + def group_ban_get_name(self, groupnumber, ban_id): + """ + Write the name of the ban entry designated by ban_id in the group designated by the + given group number to a byte array. + Call tox_group_ban_get_name_size to find out how much memory to allocate for the result. + :return name + """ + + error = c_int() + size = self.group_ban_get_name_size(groupnumber, ban_id) + name = create_string_buffer() + + result = Tox.libtoxcore.tox_group_ban_get_name(self._tox_pointer, groupnumber, ban_id, + name, byref(error)) + return str(name[:size], 'utf-8') + + def group_ban_get_time_set(self, groupnumber, ban_id): + """ + Return a time stamp indicating the time the ban was set, for the ban list entry + designated by ban_id, in the group designated by the given group number. + If either groupnumber or ban_id is invalid, the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_time_set(self._tox_pointer, groupnumber, ban_id, byref(error)) + return result diff --git a/toxygen/toxav.py b/toxygen/wrapper/toxav.py similarity index 99% rename from toxygen/toxav.py rename to toxygen/wrapper/toxav.py index 0ab891c..78ab11f 100644 --- a/toxygen/toxav.py +++ b/toxygen/wrapper/toxav.py @@ -1,7 +1,7 @@ from ctypes import c_int, POINTER, c_void_p, byref, ArgumentError, c_uint32, CFUNCTYPE, c_size_t, c_uint8, c_uint16 from ctypes import c_char_p, c_int32, c_bool, cast -from libtox import LibToxAV -from toxav_enums import * +from wrapper.libtox import LibToxAV +from wrapper.toxav_enums import * class ToxAV: diff --git a/toxygen/toxav_enums.py b/toxygen/wrapper/toxav_enums.py similarity index 100% rename from toxygen/toxav_enums.py rename to toxygen/wrapper/toxav_enums.py diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py new file mode 100644 index 0000000..6942d3a --- /dev/null +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -0,0 +1,944 @@ +TOX_USER_STATUS = { + 'NONE': 0, + 'AWAY': 1, + 'BUSY': 2, +} + +TOX_MESSAGE_TYPE = { + 'NORMAL': 0, + 'ACTION': 1, +} + +TOX_PROXY_TYPE = { + 'NONE': 0, + 'HTTP': 1, + 'SOCKS5': 2, +} + +TOX_SAVEDATA_TYPE = { + 'NONE': 0, + 'TOX_SAVE': 1, + 'SECRET_KEY': 2, +} + +TOX_ERR_OPTIONS_NEW = { + 'OK': 0, + 'MALLOC': 1, +} + +TOX_ERR_NEW = { + 'OK': 0, + 'NULL': 1, + 'MALLOC': 2, + 'PORT_ALLOC': 3, + 'PROXY_BAD_TYPE': 4, + 'PROXY_BAD_HOST': 5, + 'PROXY_BAD_PORT': 6, + 'PROXY_NOT_FOUND': 7, + 'LOAD_ENCRYPTED': 8, + 'LOAD_BAD_FORMAT': 9, +} + +TOX_ERR_BOOTSTRAP = { + 'OK': 0, + 'NULL': 1, + 'BAD_HOST': 2, + 'BAD_PORT': 3, +} + +TOX_CONNECTION = { + 'NONE': 0, + 'TCP': 1, + 'UDP': 2, +} + +TOX_ERR_SET_INFO = { + 'OK': 0, + 'NULL': 1, + 'TOO_LONG': 2, +} + +TOX_ERR_FRIEND_ADD = { + 'OK': 0, + 'NULL': 1, + 'TOO_LONG': 2, + 'NO_MESSAGE': 3, + 'OWN_KEY': 4, + 'ALREADY_SENT': 5, + 'BAD_CHECKSUM': 6, + 'SET_NEW_NOSPAM': 7, + 'MALLOC': 8, +} + +TOX_ERR_FRIEND_DELETE = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_BY_PUBLIC_KEY = { + 'OK': 0, + 'NULL': 1, + 'NOT_FOUND': 2, +} + +TOX_ERR_FRIEND_GET_PUBLIC_KEY = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_GET_LAST_ONLINE = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_QUERY = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, +} + +TOX_ERR_SET_TYPING = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_SEND_MESSAGE = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'SENDQ': 4, + 'TOO_LONG': 5, + 'EMPTY': 6, +} + +TOX_FILE_KIND = { + 'DATA': 0, + 'AVATAR': 1, +} + +TOX_FILE_CONTROL = { + 'RESUME': 0, + 'PAUSE': 1, + 'CANCEL': 2, +} + +TOX_ERR_FILE_CONTROL = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, + 'FRIEND_NOT_CONNECTED': 2, + 'NOT_FOUND': 3, + 'NOT_PAUSED': 4, + 'DENIED': 5, + 'ALREADY_PAUSED': 6, + 'SENDQ': 7, +} + +TOX_ERR_FILE_SEEK = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, + 'FRIEND_NOT_CONNECTED': 2, + 'NOT_FOUND': 3, + 'DENIED': 4, + 'INVALID_POSITION': 5, + 'SENDQ': 6, +} + +TOX_ERR_FILE_GET = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'NOT_FOUND': 3, +} + +TOX_ERR_FILE_SEND = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'NAME_TOO_LONG': 4, + 'TOO_MANY': 5, +} + +TOX_ERR_FILE_SEND_CHUNK = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'NOT_FOUND': 4, + 'NOT_TRANSFERRING': 5, + 'INVALID_LENGTH': 6, + 'SENDQ': 7, + 'WRONG_POSITION': 8, +} + +TOX_ERR_FRIEND_CUSTOM_PACKET = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'INVALID': 4, + 'EMPTY': 5, + 'TOO_LONG': 6, + 'SENDQ': 7, +} + +TOX_ERR_GET_PORT = { + 'OK': 0, + 'NOT_BOUND': 1, +} + +TOX_GROUP_PRIVACY_STATE = { + + # + # The group is considered to be public. Anyone may join the group using the Chat ID. + # + # If the group is in this state, even if the Chat ID is never explicitly shared + # with someone outside of the group, information including the Chat ID, IP addresses, + # and peer ID's (but not Tox ID's) is visible to anyone with access to a node + # storing a DHT entry for the given group. + # + 'TOX_GROUP_PRIVACY_STATE_PUBLIC': 0, + + # + # The group is considered to be private. The only way to join the group is by having + # someone in your contact list send you an invite. + # + # If the group is in this state, no group information (mentioned above) is present in the DHT; + # the DHT is not used for any purpose at all. If a public group is set to private, + # all DHT information related to the group will expire shortly. + # + 'TOX_GROUP_PRIVACY_STATE_PRIVATE': 1 +} + +TOX_GROUP_ROLE = { + + # + # May kick and ban all other peers as well as set their role to anything (except founder). + # Founders may also set the group password, toggle the privacy state, and set the peer limit. + # + 'TOX_GROUP_ROLE_FOUNDER': 0, + + # + # May kick, ban and set the user and observer roles for peers below this role. + # May also set the group topic. + # + 'TOX_GROUP_ROLE_MODERATOR': 1, + + # + # May communicate with other peers normally. + # + 'TOX_GROUP_ROLE_USER': 2, + + # + # May observe the group and ignore peers; may not communicate with other peers or with the group. + # + 'TOX_GROUP_ROLE_OBSERVER': 3 +} + +TOX_ERR_GROUP_NEW = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_NEW_OK': 0, + + # + # The group name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH. + # + 'TOX_ERR_GROUP_NEW_TOO_LONG': 1, + + # + # group_name is NULL or length is zero. + # + 'TOX_ERR_GROUP_NEW_EMPTY': 2, + + # + # TOX_GROUP_PRIVACY_STATE is an invalid type. + # + 'TOX_ERR_GROUP_NEW_PRIVACY': 3, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_NEW_INIT': 4, + + # + # The group state failed to initialize. This usually indicates that something went wrong + # related to cryptographic signing. + # + 'TOX_ERR_GROUP_NEW_STATE': 5, + + # + # The group failed to announce to the DHT. This indicates a network related error. + # + 'TOX_ERR_GROUP_NEW_ANNOUNCE': 6, +} + +TOX_ERR_GROUP_JOIN = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_JOIN_OK': 0, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_JOIN_INIT': 1, + + # + # The chat_id pointer is set to NULL or a group with chat_id already exists. This usually + # happens if the client attempts to create multiple sessions for the same group. + # + 'TOX_ERR_GROUP_JOIN_BAD_CHAT_ID': 2, + + # + # Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_JOIN_TOO_LONG': 3, +} + +TOX_ERR_GROUP_RECONNECT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_RECONNECT_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND': 1, +} + +TOX_ERR_GROUP_LEAVE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_LEAVE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_GROUP_MAX_PART_LENGTH. + # + 'TOX_ERR_GROUP_LEAVE_TOO_LONG': 2, + + # + # The parting packet failed to send. + # + 'TOX_ERR_GROUP_LEAVE_FAIL_SEND': 3, + + # + # The group chat instance failed to be deleted. This may occur due to memory related errors. + # + 'TOX_ERR_GROUP_LEAVE_DELETE_FAIL': 4, +} + +TOX_ERR_GROUP_SELF_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND': 1, +} + + +TOX_ERR_GROUP_SELF_NAME_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_GROUP_NOT_FOUND': 1, + + # + # Name length exceeded 'TOX_MAX_NAME_LENGTH. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_TOO_LONG': 2, + + # + # The length given to the set function is zero or name is a NULL pointer. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_INVALID': 3, + + # + # The name is already taken by another peer in the group. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_TAKEN': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_SELF_STATUS_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_GROUP_NOT_FOUND': 1, + + # + # An invalid type was passed to the set function. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_INVALID': 2, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_FAIL_SEND': 3 +} + +TOX_ERR_GROUP_PEER_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_PEER_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND': 2 +} + +TOX_ERR_GROUP_STATE_QUERIES = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_STATE_QUERIES_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND': 1 +} + + +TOX_ERR_GROUP_TOPIC_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_TOPIC_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND': 1, + + # + # Topic length exceeded 'TOX_GROUP_MAX_TOPIC_LENGTH. + # + 'TOX_ERR_GROUP_TOPIC_SET_TOO_LONG': 2, + + # + # The caller does not have the required permissions to set the topic. + # + 'TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS': 3, + + # + # The packet could not be created. This error is usually related to cryptographic signing. + # + 'TOX_ERR_GROUP_TOPIC_SET_FAIL_CREATE': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_SEND_MESSAGE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_TOO_LONG': 2, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_EMPTY': 3, + + # + # The message type is invalid. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_BAD_TYPE': 4, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS': 5, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND': 6 +} + +TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PEER_NOT_FOUND': 2, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_TOO_LONG': 3, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY': 4, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS': 5, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND': 6 +} + +TOX_ERR_GROUP_SEND_CUSTOM_PACKET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_TOO_LONG': 2, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_EMPTY': 3, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_PERMISSIONS': 4 +} + +TOX_ERR_GROUP_INVITE_FRIEND = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND': 1, + + # + # The friend number passed did not designate a valid friend. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND': 2, + + # + # Creation of the invite packet failed. This indicates a network related error. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_INVITE_FAIL': 3, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_INVITE_ACCEPT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_OK': 0, + + # + # The invite data is not in the expected format. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_BAD_INVITE': 1, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED': 2, + + # + # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG': 3 +} + +TOX_GROUP_JOIN_FAIL = { + + # + # You are using the same nickname as someone who is already in the group. + # + 'TOX_GROUP_JOIN_FAIL_NAME_TAKEN': 0, + + # + # The group peer limit has been reached. + # + 'TOX_GROUP_JOIN_FAIL_PEER_LIMIT': 1, + + # + # You have supplied an invalid password. + # + 'TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD': 2, + + # + # The join attempt failed due to an unspecified error. This often occurs when the group is + # not found in the DHT. + # + 'TOX_GROUP_JOIN_FAIL_UNKNOWN': 3 +} + +TOX_ERR_GROUP_FOUNDER_SET_PASSWORD = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions to set the password. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS': 2, + + # + # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_NOT_FOUND': 1, + + # + # 'TOX_GROUP_PRIVACY_STATE is an invalid type. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_INVALID': 2, + + # + # The caller does not have the required permissions to set the privacy state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS': 3, + + # + # The privacy state could not be set. This may occur due to an error related to + # cryptographic signing of the new shared state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions to set the peer limit. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS': 2, + + # + # The peer limit could not be set. This may occur due to an error related to + # cryptographic signing of the new shared state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SET': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_TOGGLE_IGNORE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND': 2 +} + +TOX_ERR_GROUP_MOD_SET_ROLE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. Note: you cannot set your own role. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND': 2, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS': 3, + + # + # The role assignment is invalid. This will occur if you try to set a peer's role to + # the role they already have. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT': 4, + + # + # The role was not successfully set. This may occur if something goes wrong with role setting': , + # or if the packet fails to send. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION': 5 +} + +TOX_ERR_GROUP_MOD_REMOVE_PEER = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND': 2, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS': 3, + + # + # The peer could not be removed from the group. + # + # If a ban was set': , this error indicates that the ban entry could not be created. + # This is usually due to the peer's IP address already occurring in the ban list. It may also + # be due to the entry containing invalid peer information': , or a failure to cryptographically + # authenticate the entry. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_MOD_REMOVE_BAN = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS': 2, + + # + # The ban entry could not be removed. This may occur if ban_id does not designate + # a valid ban entry. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND': 4 +} + +TOX_GROUP_MOD_EVENT = { + + # + # A peer has been kicked from the group. + # + 'TOX_GROUP_MOD_EVENT_KICK': 0, + + # + # A peer has been banned from the group. + # + 'TOX_GROUP_MOD_EVENT_BAN': 1, + + # + # A peer as been given the observer role. + # + 'TOX_GROUP_MOD_EVENT_OBSERVER': 2, + + # + # A peer has been given the user role. + # + 'TOX_GROUP_MOD_EVENT_USER': 3, + + # + # A peer has been given the moderator role. + # + 'TOX_GROUP_MOD_EVENT_MODERATOR': 4, +} + +TOX_ERR_GROUP_BAN_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_BAN_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND': 1, + + # + # The ban_id does not designate a valid ban list entry. + # + 'TOX_ERR_GROUP_BAN_QUERY_BAD_ID': 2, +} + +TOX_PUBLIC_KEY_SIZE = 32 + +TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 + +TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 + +TOX_MAX_MESSAGE_LENGTH = 1372 + +TOX_GROUP_MAX_TOPIC_LENGTH = 512 + +TOX_GROUP_MAX_PART_LENGTH = 128 + +TOX_GROUP_MAX_GROUP_NAME_LENGTH = 48 + +TOX_GROUP_MAX_PASSWORD_SIZE = 32 + +TOX_GROUP_CHAT_ID_SIZE = 32 + +TOX_GROUP_PEER_PUBLIC_KEY_SIZE = 32 + +TOX_MAX_NAME_LENGTH = 128 + +TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 + +TOX_SECRET_KEY_SIZE = 32 + +TOX_FILE_ID_LENGTH = 32 + +TOX_HASH_LENGTH = 32 + +TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/toxencryptsave.py b/toxygen/wrapper/toxencryptsave.py similarity index 97% rename from toxygen/toxencryptsave.py rename to toxygen/wrapper/toxencryptsave.py index b579e21..31de085 100644 --- a/toxygen/toxencryptsave.py +++ b/toxygen/wrapper/toxencryptsave.py @@ -1,6 +1,6 @@ -import libtox +from wrapper import libtox from ctypes import c_size_t, create_string_buffer, byref, c_int, ArgumentError, c_char_p, c_bool -from toxencryptsave_enums_and_consts import * +from wrapper.toxencryptsave_enums_and_consts import * class ToxEncryptSave: diff --git a/toxygen/toxencryptsave_enums_and_consts.py b/toxygen/wrapper/toxencryptsave_enums_and_consts.py similarity index 100% rename from toxygen/toxencryptsave_enums_and_consts.py rename to toxygen/wrapper/toxencryptsave_enums_and_consts.py