This commit is contained in:
emdee 2022-10-08 02:46:23 +00:00
parent 9e037f13c0
commit be6eb0e2a9
18 changed files with 135 additions and 71 deletions

25
ToDo.md
View file

@ -16,24 +16,29 @@ The code is in there but it's not working.
I may have broken it trying to wire up the ability to
set the audio device from the command line.
## Group peer_id
## Groups
There has been a change of API on a field named group.peer_id
The code is broken in places because I have not seen the path
to change from the old API ro the new one.
1. peer_id There has been a change of API on a field named
```group.peer_id``` The code is broken in places because I have not
seen the path to change from the old API ro the new one.
2. There is no way to delete a group in the UI
3. Distinguish between Frieds, Groups and Whispers in the UI.
## Plugin system
Needs better documentation and checking.
There's something broken in the way some of them plug into Qt menus.
1. Needs better documentation and checking.
Should the plugins be in toxygen or a separate repo?
2. There's something broken in the way some of them plug into Qt menus.
3. Should the plugins be in toxygen or a separate repo?
## check toxygen_wrapper
I've broken out toxygen_wrapper to be standalone,
https://git.plastiras.org/emdee/toxygen_wrapper
but the tox.py needs each call double checking.
1. I've broken out toxygen_wrapper to be standalone,
https://git.plastiras.org/emdee/toxygen_wrapper but the tox.py
needs each call double checking.

View file

@ -38,7 +38,7 @@ def setup_logging(oArgs):
setattr(oArgs, 'log_oFd', oFd)
aKw['stream'] = oFd
coloredlogs.install(**aKw)
else:
aKw = dict(level=oArgs.loglevel,
format='%(name)s %(levelname)-4s %(message)s')
@ -162,6 +162,7 @@ class App:
LOG.info("Starting toxygen version " +version)
self._version = version
self._tox = None
self._app = self._settings = self._profile_manager = None
self._plugin_loader = self._messenger = None
self._tox = self._ms = self._init = self._main_loop = self._av_loop = None
@ -268,7 +269,7 @@ class App:
if hasattr(oArgs, 'log_oFd'):
oArgs.log_oFd.close()
delattr(oArgs, 'log_oFd')
# failsafe: segfaults on exit
if hasattr(self, '_tox'):
if self._tox and hasattr(self._tox, 'kill'):
@ -777,6 +778,7 @@ class App:
retval = tox_factory(data=data, settings=settings_,
args=self._args, app=self)
LOG.debug("_create_tox succeeded")
self._tox = retval
return retval
def _force_exit(self, retval=0):
@ -925,7 +927,7 @@ class App:
shuffle(lElts)
LOG.debug(f"_test_bootstrap #Elts={len(lElts)}")
LOG.trace(f"_test_bootstrap lElts={lElts[:8]}")
shuffle(env['lElts'])
shuffle(env['lElts'])
for host,port,key in lElts[:8]:
try:
assert len(key) == 64, key

View file

@ -101,7 +101,7 @@ class AV(common.tox_save.ToxAvSave):
def accept_call(self, friend_number, audio_enabled, video_enabled):
# obsolete
return call_accept_call(self, friend_number, audio_enabled, video_enabled)
def call_accept_call(self, friend_number, audio_enabled, video_enabled):
LOG.debug(f"call_accept_call from {friend_number} {self._running}" +
f"{audio_enabled} {video_enabled}")
@ -304,7 +304,7 @@ class AV(common.tox_save.ToxAvSave):
LOG.info("start_video_thread " \
+f" device: {s['video']['device']}" \
+f" supported: {s['video']['width']} {s['video']['height']}")
self._video_running = True
self._video_thread = BaseThread(target=self.send_video,
name='_video_thread')

View file

@ -114,7 +114,7 @@ class CallsManager:
util_ui.tr('ERROR Accepting call from {friend_number}'))
else:
self._main_screen.active_call()
finally:
# does not terminate call - just the av_widget
if friend_number in self._incoming_calls:
@ -127,7 +127,7 @@ class CallsManager:
pass
LOG.debug(f" closed self._call_widgets[{friend_number}]")
def stop_call(self, friend_number, by_friend):
"""
Stop call with friend

View file

@ -44,7 +44,7 @@ class ContactsManager(ToxSave):
try:
self._ms._log(s)
except: pass
def get_contact(self, num):
if num < 0 or num >= len(self._contacts):
return None
@ -254,10 +254,14 @@ class ContactsManager(ToxSave):
group = self.get_group_by_number(group_number)
peer = group.get_peer_by_id(peer_id)
if peer: # broken
if not self.check_if_contact_exists(peer.public_key):
self.add_group_peer(group, peer)
return self.get_contact_by_tox_id(peer.public_key)
if hasattr(peer, 'public_key'):
LOG.error(f'no peer public_key ' + repr(dir(peer)))
else:
if not self.check_if_contact_exists(peer.public_key):
self.add_group_peer(group, peer)
return self.get_contact_by_tox_id(peer.public_key)
else:
LOG.warn(f'no peer group_number={group_number}')
def check_if_contact_exists(self, tox_id):
return any(filter(lambda c: c.tox_id == tox_id, self._contacts))
@ -455,7 +459,7 @@ class ContactsManager(ToxSave):
title = 'Friend failed'
text = 'Friend failed sending friend request'
retval = text
except Exception as ex: # wrong data
title = 'Friend add exception'
text = 'Friend request exception with ' + str(ex)
@ -466,7 +470,7 @@ class ContactsManager(ToxSave):
text = util_ui.tr(text)
util_ui.message_box(text, title)
return retval
def process_friend_request(self, tox_id, message):
"""
Accept or ignore friend request

View file

@ -86,7 +86,7 @@ class GroupChat(contact.Contact, ToxSave):
if peer_id > self._peers_limit:
LOG_WARN(f"add_peer id={peer_id} > {self._peers_limit}")
return
peer = GroupChatPeer(peer_id,
self._tox.group_peer_get_name(self._number, peer_id),
self._tox.group_peer_get_status(self._number, peer_id),

View file

@ -7,6 +7,9 @@ from groups.group_invite import GroupInvite
import wrapper.toxcore_enums_and_consts as constants
from wrapper.toxcore_enums_and_consts import *
global LOG
import logging
LOG = logging.getLogger('app.'+'gs')
class GroupsService(tox_save.ToxSave):
@ -19,6 +22,8 @@ class GroupsService(tox_save.ToxSave):
self._widgets_factory_provider = widgets_factory_provider
self._group_invites = []
self._screen = None
# maybe just use self
self._tox = tox
def set_tox(self, tox):
super().set_tox(tox)
@ -30,7 +35,11 @@ class GroupsService(tox_save.ToxSave):
# -----------------------------------------------------------------------------------------------------------------
def create_new_gc(self, name, privacy_state, nick, status):
group_number = self._tox.group_new(privacy_state, name, nick, status)
try:
group_number = self._tox.group_new(privacy_state, name, nick, status)
except Exception as e:
LOG.error(f"create_new_gc {e}")
return
if group_number == -1:
return
@ -48,8 +57,9 @@ class GroupsService(tox_save.ToxSave):
# -----------------------------------------------------------------------------------------------------------------
def leave_group(self, group_number):
self._tox.group_leave(group_number)
self._contacts_manager.delete_group(group_number)
if type(group_number) == int:
self._tox.group_leave(group_number)
self._contacts_manager.delete_group(group_number)
def disconnect_from_group(self, group_number):
self._tox.group_disconnect(group_number)
@ -73,7 +83,7 @@ class GroupsService(tox_save.ToxSave):
e = f"Friend not connected friend_number={friend_number}"
util_ui.message_box(title +'\n' +str(e), title)
return
try:
self._tox.group_invite_friend(group_number, friend_number)
except Exception as e:
@ -246,8 +256,14 @@ class GroupsService(tox_save.ToxSave):
self._group_invites.remove(invite)
def _join_gc_via_invite(self, invite_data, friend_number, nick, status, password):
group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password)
self._add_new_group_by_number(group_number)
if nick is None: nick = ''
if invite_data is None: invite_data = ''
try:
group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password)
except Exception as e:
LOG.error(f"_join_gc_via_invite {e}")
else:
self._add_new_group_by_number(group_number)
def _update_invites_button_state(self):
self._main_screen.update_gc_invites_button_state()

View file

@ -306,13 +306,13 @@ lKEEP_SETTINGS = ['uri',
'loglevel',
'logfile',
'mode',
# dunno
'audio_input',
'audio_output',
'audio',
'video',
'ipv6_enabled',
'udp_enabled',
'local_discovery_enabled',
@ -378,15 +378,16 @@ def main(lArgs):
#setattr(aArgs, 'video', setup_video(oArgs))
aArgs.video = setup_video(oArgs)
assert 'video' in aArgs.__dict__
#setattr(aArgs, 'audio', setup_audio(oArgs))
aArgs.audio = setup_audio(oArgs)
assert 'audio' in aArgs.__dict__
oArgs = aArgs
toxygen = app.App(__version__, oArgs)
global oAPP
oAPP = toxygen
__builtins__['app'] = toxygen
i = toxygen.iMain()
return i

View file

@ -167,7 +167,7 @@ def friend_status_message(contacts_manager, messenger):
friend = contacts_manager.get_friend_by_number(friend_number)
key = f"friend_number={friend_number}"
if bTooSoon(key, sSlot, 10): return
invoke_in_main_thread(friend.set_status_message, str(status_message, 'utf-8'))
LOG_DEBUG(f'User #{friend_number} has new status message')
invoke_in_main_thread(messenger.send_messages, friend_number)
@ -480,7 +480,7 @@ def group_private_message(window, tray, tox, messenger, settings, profile):
if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['MESSAGE'])
icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png')
if tray and hasattr(tray, 'setIcon'):
if tray and hasattr(tray, 'setIcon'):
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
return wrapped

View file

@ -119,11 +119,11 @@ class InitThread(BaseThread):
args=list(),
kwargs=dict(lElts=None, oThread=self, iMax=2)
).start()
if self._is_first_start:
LOG_INFO('starting plugins')
self._plugin_loader.load()
except Exception as e:
LOG_DEBUG(f"InitThread run: ERROR {e}")
pass
@ -157,7 +157,7 @@ class ToxAVIterateThread(BaseQThread):
def __init__(self, toxav):
super().__init__()
self._toxav = toxav
def run(self):
LOG_DEBUG('ToxAVIterateThread run: ')
while not self._stop_thread:

View file

@ -50,7 +50,7 @@ class GroupBansScreen(CenteredWidget):
def _retranslate_ui(self):
# self.setWindowTitle(util_ui.tr('Bans list for group "{}"').format(self._group.name))
pass
def _refresh_bans_list(self):
self.bansListWidget.clear()
can_cancel_ban = self._group.is_self_moderator_or_founder()

View file

@ -27,6 +27,7 @@ class GroupInvitesScreen(CenteredWidget):
self._groups_service = groups_service
self._profile = profile
self._contacts_provider = contacts_provider
self._tox = self._groups_service._tox
uic.loadUi(util.get_views_path('group_invites_screen'), self)
@ -68,6 +69,8 @@ class GroupInvitesScreen(CenteredWidget):
password = self.passwordLineEdit.text()
status = self.statusComboBox.currentIndex()
if not nick:
nick = self._tox.self_get_name()
selected_invites = self._get_selected_invites()
for invite in selected_invites:
self._groups_service.accept_group_invite(invite, nick, status, password)
@ -90,7 +93,7 @@ class GroupInvitesScreen(CenteredWidget):
for index in range(items_count):
list_item = self.invitesListWidget.item(index)
item_widget = self.invitesListWidget.itemWidget(list_item)
if item_widget.is_selected():
if item_widget and item_widget.is_selected():
selected.append(all_invites[index])
return selected

View file

@ -3,7 +3,6 @@ from PyQt5 import uic
import utils.util as util
import utils.ui as util_ui
class GroupManagementScreen(CenteredWidget):
def __init__(self, groups_service, group):
@ -21,6 +20,7 @@ class GroupManagementScreen(CenteredWidget):
self.privacyStateComboBox.setCurrentIndex(1 if self._group.is_private else 0)
self.peersLimitSpinBox.setValue(self._group.peers_limit)
self.deletePushButton.clicked.connect(self._delete)
self.savePushButton.clicked.connect(self._save)
def _retranslate_ui(self):
@ -28,12 +28,21 @@ class GroupManagementScreen(CenteredWidget):
self.passwordLabel.setText(util_ui.tr('Password:'))
self.peerLimitLabel.setText(util_ui.tr('Peer limit:'))
self.privacyStateLabel.setText(util_ui.tr('Privacy state:'))
self.deletePushButton.setText(util_ui.tr('Delete'))
self.savePushButton.setText(util_ui.tr('Save'))
self.privacyStateComboBox.clear()
self.privacyStateComboBox.addItem(util_ui.tr('Public'))
self.privacyStateComboBox.addItem(util_ui.tr('Private'))
def _delete(self):
self._groups_service.leave_group(self._group.number)
self.close()
def _disconnect(self):
self._groups_service.disconnect_from_group(self._group.number)
self.close()
def _save(self):
password = self.passwordLineEdit.text()
privacy_state = self.privacyStateComboBox.currentIndex()

View file

@ -76,13 +76,16 @@ class MainWindow(QtWidgets.QMainWindow):
self._contacts_manager = None
self._tray = tray
self._app = app
self._tox = app._tox
self._widget_factory = None
self._modal_window = None
self._plugins_loader = None
self.setAcceptDrops(True)
self._saved = False
self._smiley_window = None
self._profile = self._toxes = self._messenger = None
self._profile = None
self._toxes = None
self._messenger = None
self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None
self._should_show_group_peers_list = False
self.initUI()
@ -91,6 +94,7 @@ class MainWindow(QtWidgets.QMainWindow):
# take a rough guess of 2/3 the default width at the default font
iMAX = settings['width'] * 2/3 / settings['message_font_size']
self._me = LogDialog(self, app)
self._pe = None
def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader,
file_transfer_handler, history_loader, calls_manager, groups_service, toxes, app):
@ -159,6 +163,8 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionLog_console = QtWidgets.QAction(window)
self.actionLog_console.setObjectName("actionLog_console")
self.actionPython_console = QtWidgets.QAction(window)
self.actionPython_console.setObjectName("actionLog_console")
self.updateSettings = QtWidgets.QAction(window)
self.actionSettings = QtWidgets.QAction(window)
self.actionSettings.setObjectName("actionSettings")
@ -196,6 +202,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.menuPlugins.addAction(self.reloadPlugins)
self.menuPlugins.addAction(self.reloadToxchat)
self.menuPlugins.addAction(self.actionLog_console)
self.menuPlugins.addAction(self.actionPython_console)
self.menuAbout.addAction(self.actionAbout_program)
@ -211,6 +218,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionQuit_program.triggered.connect(self.quit_program)
self.actionAbout_program.triggered.connect(self.about_program)
self.actionLog_console.triggered.connect(self.log_console)
self.actionPython_console.triggered.connect(self.python_console)
self.actionNetwork.triggered.connect(self.network_settings)
self.actionAdd_friend.triggered.connect(self.add_contact_triggered)
self.createGC.triggered.connect(self.create_gc)
@ -264,6 +272,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionNetwork.setText(util_ui.tr("Network"))
self.actionAbout_program.setText(util_ui.tr("About program"))
self.actionLog_console.setText(util_ui.tr("Console Log"))
self.actionPython_console.setText(util_ui.tr("Python Console"))
self.actionTest_tox.setText(util_ui.tr("Bootstrap"))
self.actionTest_socks.setText(util_ui.tr("Test program"))
self.actionQuit_program.setText(util_ui.tr("Quit program"))
@ -554,6 +563,18 @@ class MainWindow(QtWidgets.QMainWindow):
def log_console(self):
self._me.show()
def python_console(self):
try:
if not self._pe:
from pyqtconsole.console import PythonConsole
self._pe = PythonConsole(sFont="Courier New", bBold=True)
self._pe.show()
self._pe.eval_queued()
# self._pe.eval_in_thread()
except Exception as e:
LOG.debug(e)
self._me.show()
def about_program(self):
# TODO: replace with window
text = util_ui.tr('Toxygen is Tox client written in Python.\nVersion: ')

View file

@ -43,7 +43,7 @@ class AddContact(CenteredWidget):
if self._bootstrap:
return
self._bootstrap = True
def _add_friend(self):
if self._adding:
return
@ -497,7 +497,7 @@ class AudioSettings(CenteredWidget):
uic.loadUi(get_views_path('audio_settings_screen'), self)
self._update_ui()
self.center()
def closeEvent(self, event):
if 'audio' not in self._settings:
ex = f"self._settings=id(self._settings) {self._settings!r}"
@ -618,7 +618,7 @@ class VideoSettings(CenteredWidget):
if 'device' not in self._settings['video']:
LOG.warn(f"'device' not in self._settings['video']: {self._settings!r}")
self._settings['video']['device'] = self._devices[-1]
self._settings['video']['device'] = self._devices[-1]
iIndex = self._settings['video']['device']
try:
index = self._devices.index(iIndex)
@ -628,7 +628,7 @@ class VideoSettings(CenteredWidget):
se = f"Video devices index error: index={iIndex} {e}"
LOG.warn(se)
# util_ui.message_box(se, util_ui.tr(f"ERROR: Video devices error"))
self._settings['video']['device'] = self._devices[-1]
self._settings['video']['device'] = self._devices[-1]
self._retranslate_ui()

View file

@ -412,4 +412,4 @@ class Settings(dict):
if key in aArgs.__dict__ and info[key] != val:
aRet[key] = val
return aRet

View file

@ -3,7 +3,7 @@ import os
import sys
from ctypes import CDLL
# You need a libs directory beside this directory
# You need a libs directory beside this directory
# and you need to link your libtoxcore.so and libtoxav.so
# and libtoxencryptsave.so into ../libs/
# Link all 3 to libtoxcore.so if you have only libtoxcore.so
@ -13,7 +13,7 @@ try:
except ImportError:
sLIBS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)),
'libs')
class LibToxCore:
def __init__(self):
@ -40,11 +40,11 @@ class LibToxCore:
class LibToxAV:
def __init__(self):
platform = util.get_platform()
if platform == 'Windows':
platform = sys.platform
if platform == 'win32':
# on Windows av api is in libtox.dll
self._libtoxav = CDLL(os.path.join(sLIBS_DIR, 'libtox.dll'))
elif platform == 'Darwin':
elif platform == 'darwin':
self._libtoxav = CDLL('libtoxcore.dylib')
else:
libFile = os.path.join(sLIBS_DIR, 'libtoxav.so')

View file

@ -172,7 +172,7 @@ class Tox:
LOG_ERROR(f"tox_kill {e!s}")
else:
LOG_DEBUG(f"tox_kill")
# -----------------------------------------------------------------------------------------------------------------
# Startup options
# -----------------------------------------------------------------------------------------------------------------
@ -287,7 +287,7 @@ class Tox:
LOG_ERROR(f"libtoxcore.tox_bootstrap {e}")
# dunno
raise
tox_err_bootstrap = tox_err_bootstrap.value
if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']:
return bool(result)
@ -776,7 +776,7 @@ class Tox:
if friend_list is None:
friend_list = create_string_buffer(sizeof(c_uint32) * friend_list_size)
friend_list = POINTER(c_uint32)(friend_list)
LOG_DEBUG(f"tox_self_get_friend_list")
LOG_TRACE(f"tox_self_get_friend_list")
Tox.libtoxcore.tox_self_get_friend_list(self._tox_pointer, friend_list)
return friend_list[0:friend_list_size]
@ -1341,7 +1341,7 @@ class Tox:
POINTER(None)())
self.file_recv_control_cb = None
return
LOG_DEBUG(f"tox_callback_file_recv_control")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p)
self.file_recv_control_cb = c_callback(callback)
@ -1607,7 +1607,7 @@ class Tox:
POINTER(None)())
self.file_recv_cb = None
return
LOG_DEBUG(f"tox_callback_file_recv")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p)
self.file_recv_cb = c_callback(callback)
@ -1640,7 +1640,7 @@ class Tox:
POINTER(None)())
self.file_recv_chunk_cb = None
return
LOG_DEBUG(f"tox_callback_file_recv_chunk")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p)
self.file_recv_chunk_cb = c_callback(callback)
@ -1764,7 +1764,7 @@ class Tox:
self.friend_lossless_packet_cb = None
self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, POINTER(None)())
return
LOG_DEBUG(f"callback_friend_lossless_packet")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
self.friend_lossless_packet_cb = c_callback(callback)
@ -2387,7 +2387,6 @@ class Tox:
error = c_int()
buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE)
LOG_DEBUG(f"tox_group_get_chat_id")
result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer,
group_number,
buff, byref(error))
@ -2575,23 +2574,27 @@ class Tox:
# -----------------------------------------------------------------------------------------------------------------
def group_send_custom_packet(self, group_number, lossless, data):
"""
Send a custom packet to the group.
"""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 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.
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.
Unless latency is an issue or message reliability is not
important, it is recommended that you use lossless custom
packets.
:param group_number: 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()