This commit is contained in:
emdee@spm.plastiras.org 2024-02-03 04:34:10 +00:00
parent e4b1b9c4d8
commit 3ce822fc27
18 changed files with 198 additions and 68 deletions

View file

@ -85,3 +85,7 @@ Work on Tox on this project is suspended until the
This will probably be ported to Qt6 using qtpy
https://github.com/spyder-ide/qtpy .
Up-to-date code is on https://git.plastiras.org/emdee/toxygen
Work on this project is suspended until the
[MultiDevice](https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC) problem is solved. Fork me!

51
pyproject.tom Normal file
View file

@ -0,0 +1,51 @@
[project]
name = "stem_examples"
description = "examples of using stem"
authors = [{ name = "emdee", email = "emdee@spm.plastiras.org" } ]
requires-python = ">=3.6"
dependencies = [
'stem',
]
keywords = ["stem", "python3", "tor"]
classifiers = [
"License :: OSI Approved",
"Operating System :: POSIX :: BSD :: FreeBSD",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
]
#
dynamic = ["version", "readme", ] # cannot be dynamic ['license']
[project.scripts]
toxygen = "toxygen.toxygen.main:main"
#[project.license]
#file = "LICENSE.md"
[project.urls]
repository = "https://git.plastiras.org/emdee/toxygen"
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[tool.setuptools.dynamic]
version = {attr = "stem_examples.__version__"}
readme = {file = ["README.md", "ToDo.txt"]}
[tool.setuptools]
packages = ["toxygen"]
[tool.setuptools.packages.find]
where = "toxygen"
[tool.setuptools.packages.package-data]
"*" = ["*.ui"]

View file

@ -4,3 +4,8 @@ numpy
opencv-python
pydenticon
cv2
gevent
greenlet
pydenticon
pyqtconsole
toxygen_wrapper

View file

@ -5,8 +5,7 @@ from setuptools.command.install import install
version = '1.0.0'
MODULES = ['argparse', 'PyQt5', 'PyAudio', 'numpy', 'opencv-python', 'cv2',
'pydenticon', 'pyqtconsole', 'toxygen_wrapper'] # qweechat
MODULES = open('requirements.txt', 'rt').readlines()
def get_packages():
directory = os.path.join(os.path.dirname(__file__), 'tox_wrapper')
@ -35,11 +34,18 @@ setup(name='Toxygen',
include_package_data=True,
classifiers=[
'Programming Language :: Python :: 3 :: Only',
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
'Programming Language :: Python :: 3.11',
],
entry_points={
'console_scripts': ['toxygen=toxygen.main:main']
},
package_data={"": ["*.ui"],},
cmdclass={
'install': InstallScript,
},

View file

@ -1,5 +1,4 @@
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
import pyaudio
import time
import threading
import itertools
@ -8,6 +7,8 @@ from tox_wrapper.toxav_enums import *
from tox_wrapper.tests import support_testing as ts
from tox_wrapper.tests.support_testing import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
with ts.ignoreStderr():
import pyaudio
from av import screen_sharing
from av.call import Call
import common.tox_save

View file

@ -161,6 +161,12 @@ class BaseContact:
# Widgets
def init_widget(self):
# File "/mnt/o/var/local/src/toxygen/toxygen/contacts/contacts_manager.py", line 252, in filtration_and_sorting
# contact.set_widget(item_widget)
# File "/mnt/o/var/local/src/toxygen/toxygen/contacts/contact.py", line 320, in set_widget
if not self._widget:
LOG_WARN("BC.init_widget self._widget is NULL")
return
self._widget.name.setText(self._name)
self._widget.status_message.setText(self._status_message)
if hasattr(self._widget, 'kind'):

View file

@ -33,9 +33,11 @@ class ContactProvider(tox_save.ToxSave):
if friend is not None:
return friend
friend = self._friend_factory.create_friend_by_public_key(public_key)
self._add_to_cache(public_key, friend)
LOG_INFO(f"CP.get_friend_by_public_key ADDED {friend} ")
if friend is None:
LOG_WARN(f"CP.get_friend_by_public_key NULL {friend} ")
else:
self._add_to_cache(public_key, friend)
LOG_DEBUG(f"CP.get_friend_by_public_key ADDED {friend} ")
return friend
def get_all_friends(self):
@ -72,7 +74,7 @@ class ContactProvider(tox_save.ToxSave):
def get_group_by_number(self, group_number):
group = None
try:
LOG_INFO(f"CP.CP.group_get_number {group_number} ")
# LOG_DEBUG(f"CP.CP.group_get_number {group_number} ")
# original code
chat_id = self._tox.group_get_chat_id(group_number)
if chat_id is None:
@ -110,7 +112,7 @@ class ContactProvider(tox_save.ToxSave):
return group
group = self._group_factory.create_group_by_public_key(public_key)
if group is None:
LOG_ERROR(f"get_group_by_public_key NULL group public_key={public_key}")
LOG_WARN(f"get_group_by_public_key NULL group public_key={public_key}")
else:
self._add_to_cache(public_key, group)
@ -123,13 +125,17 @@ class ContactProvider(tox_save.ToxSave):
def get_group_peer_by_id(self, group, peer_id):
peer = group.get_peer_by_id(peer_id)
if peer:
if peer is not None:
return self._get_group_peer(group, peer)
LOG_WARN(f"get_group_peer_by_id peer_id={peer_id}")
return None
def get_group_peer_by_public_key(self, group, public_key):
peer = group.get_peer_by_public_key(public_key)
if peer:
if peer is not None:
return self._get_group_peer(group, peer)
LOG_WARN(f"get_group_peer_by_public_key public_key={public_key}")
return None
# All contacts

View file

@ -99,6 +99,10 @@ class ContactsManager(ToxSave):
LOG.warn(f"ERROR NULL {self._contacts[self._active_contact]} {contact.tox_id}")
return False
if not hasattr(contact, 'tox_id'):
LOG.warn(f"ERROR is_contact_active no contact.tox_id {type(contact)} contact={contact}")
return False
return self._contacts[self._active_contact].tox_id == contact.tox_id
# Reconnection support
@ -142,7 +146,11 @@ class ContactsManager(ToxSave):
current_contact.curr_text = self._screen.messageEdit.toPlainText()
except:
pass
# IndexError: list index out of range
if value >= len(self._contacts):
LOG.warn("CM.set_active value too big: {{self._contacts}}")
return
contact = self._contacts[value]
self._subscribe_to_events(contact)
contact.remove_invalid_unsent_files()
@ -171,9 +179,8 @@ class ContactsManager(ToxSave):
# self._screen.call_finished()
self._set_current_contact_data(contact)
self._active_contact_changed(contact)
except Exception as ex: # no friend found. ignore
LOG.warn(f"no friend found. Friend value: {value!s}")
LOG.error('in set active: ' + str(ex))
except Exception as e: # no friend found. ignore
LOG.warn(f"CM.set_active EXCEPTION value:{value} len={len(self._contacts)} {e}")
# gulp raise
active_contact = property(get_active, set_active)
@ -249,6 +256,9 @@ class ContactsManager(ToxSave):
for index, contact in enumerate(self._contacts):
list_item = self._screen.friends_list.item(index)
item_widget = self._screen.friends_list.itemWidget(list_item)
if not item_widget:
LOG_WARN("CM.filtration_and_sorting( item_widget is NULL")
continue
contact.set_widget(item_widget)
for index, friend in enumerate(self._contacts):
@ -287,16 +297,16 @@ class ContactsManager(ToxSave):
def get_or_create_group_peer_contact(self, group_number, peer_id):
group = self.get_group_by_number(group_number)
peer = group.get_peer_by_id(peer_id)
if peer is None: # broken? is 0 allowed?
if not hasattr(peer, 'public_key') or not 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} peer_id={peer_id}')
return peer
if peer is None:
LOG.warn(f'get_or_create_group_peer_contact group_number={group_number} peer_id={peer_id} peer={peer}')
return None
LOG.debug(f'get_or_create_group_peer_contact group_number={group_number} peer_id={peer_id} peer={peer}')
if not self.check_if_contact_exists(peer.public_key):
contact = self.add_group_peer(group, peer)
# dunno
return contact
# me - later wrong kind of object?
return self.get_contact_by_tox_id(peer.public_key)
def check_if_contact_exists(self, tox_id):
return any(filter(lambda c: c.tox_id == tox_id, self._contacts))
@ -436,6 +446,7 @@ class ContactsManager(ToxSave):
self._contacts.append(contact)
contact.reset_avatar(self._settings['identicons'])
self._save_profile()
return contact
def remove_group_peer_by_id(self, group, peer_id):
peer = group.get_peer_by_id(peer_id)

View file

@ -107,7 +107,7 @@ class GroupChat(contact.Contact, ToxSave):
return peers[0]
else:
LOG_WARN(f"get_peer_by_id empty peers for {peer_id}")
return []
return None
def get_peer_by_public_key(self, public_key):
peers = list(filter(lambda p: p.public_key == public_key, self._peers))
@ -117,7 +117,7 @@ class GroupChat(contact.Contact, ToxSave):
return peers[0]
else:
LOG_WARN(f"get_peer_by_public_key empty peers for {public_key}")
return []
return None
def remove_all_peers_except_self(self):
self._peers = self._peers[:1]

View file

@ -72,7 +72,11 @@ def setup_default_audio():
def setup_video(oArgs):
video = setup_default_video()
if oArgs.video_input == '-1':
# this is messed up - no video_input in oArgs
# parser.add_argument('--video_input', type=str,)
if not hasattr(oArgs, 'video_input'):
video['device'] = video['output_devices'][0]
elif oArgs.video_input == '-1':
video['device'] = video['output_devices'][1]
else:
video['device'] = oArgs.video_input
@ -165,7 +169,7 @@ def setup_audio(oArgs):
def setup_default_video():
default_video = ["-1"]
default_video.extend(ts.get_video_indexes())
LOG.info(f"Video input choices: {default_video!r}")
LOG.info(f"Video input choices: {default_video}")
video = {'device': -1, 'width': 320, 'height': 240, 'x': 0, 'y': 0}
video['output_devices'] = default_video
return video
@ -179,7 +183,7 @@ def main_parser(_=None, iMode=2):
lIpV6Choices=[bIpV6, 'False']
audio = setup_default_audio()
default_video = setup_default_video()
default_video = setup_default_video()['output_devices']
parser = ts.oMainArgparser()
parser.add_argument('--version', action='store_true', help='Prints Toxygen version')
@ -225,7 +229,7 @@ def main_parser(_=None, iMode=2):
help='Update program (broken)')
parser.add_argument('--video_input', type=str,
default=-1,
choices=default_video['output_devices'],
choices=default_video,
help="Video input device number - /dev/video?")
parser.add_argument('--audio_input', type=str,
default=oPYA.get_default_input_device_info()['name'],
@ -266,6 +270,8 @@ lKEEP_SETTINGS = ['uri',
'ipv6_enabled',
'udp_enabled',
'local_discovery_enabled',
'trace_enabled',
'theme',
'network',
'message_font_size',
@ -283,9 +289,11 @@ lKEEP_SETTINGS = ['uri',
class A(): pass
def main(lArgs):
def main(lArgs=None):
global oPYA
from argparse import Namespace
if lArgs is None:
lArgs = sys.argv[1:]
parser = main_parser()
default_ns = parser.parse_args([])
oArgs = parser.parse_args(lArgs)
@ -328,7 +336,10 @@ def main(lArgs):
oApp = app.App(__version__, oArgs)
# for pyqtconsole
__builtins__.app = oApp
try:
setattr(__builtins__, 'app', oApp)
except Exception as e:
pass
i = oApp.iMain()
return i

View file

@ -192,21 +192,33 @@ class Messenger(tox_save.ToxSave):
return
if group.number < 0:
return
if peer_id and peer_id < 0:
if peer_id is not None and peer_id < 0:
return
assert_main_thread()
# FixMe: peer_id is None?
group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id)
# group_peer_contact now may be None
group = self._get_group_by_number(group_number)
messages = self._split_message(text.encode('utf-8'))
# FixMe: peer_id is None?
group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id)
if group_peer_contact is None:
LOG.warn("M.group_send_private_message group_peer_contact is None")
return
# group_peer_contact now may be None
t = util.get_unix_time()
for message in messages:
self._tox.group_send_private_message(group_number, peer_id, message_type, message)
bRet = self._tox.group_send_private_message(group_number, peer_id, message_type, message)
if not bRet:
LOG.warn("M.group_send_private_messag failed")
continue
message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER'])
message = OutgoingTextMessage(text, message_author, t, message_type)
group_peer_contact.append_message(message)
# AttributeError: 'GroupChatPeer' object has no attribute 'append_message'
if not hasattr(group_peer_contact, 'append_message'):
LOG.warn("M. group_peer_contact has no append_message group_peer_contact={group_peer_contact}")
else:
group_peer_contact.append_message(message)
if not self._contacts_manager.is_contact_active(group_peer_contact):
return
self._create_message_item(message)

View file

@ -473,7 +473,11 @@ def group_private_message(window, tray, tox, messenger, settings, profile):
if window.isActiveWindow():
return
bl = settings['notify_all_gc'] or profile.name in message
name = tox.group_peer_get_name(group_number, peer_id)
try:
name = tox.group_peer_get_name(group_number, peer_id)
except Exception as e:
LOG_WARN("tox.group_peer_get_name {group_number} {peer_id}")
name = ''
if settings['notifications'] and settings['tray_icon'] \
and profile.status != TOX_USER_STATUS['BUSY'] \
and (not settings.locked) and bl:

View file

@ -69,12 +69,9 @@ def tox_factory(data=None, settings=None, args=None, app=None):
if 'trace_enabled' in settings and not settings['trace_enabled']:
LOG_DEBUG("settings['trace_enabled' disabled" )
elif tox_options._options_pointer:
c_callback = CFUNCTYPE(None, c_void_p, c_int, c_char_p, c_int, c_char_p, c_char_p, c_void_p)
tox_options.self_logger_cb = c_callback(ts.tox_log_cb)
tox_wrapper.tox.Tox.libtoxcore.tox_options_set_log_callback(
tox_options._options_pointer,
tox_options.self_logger_cb)
elif tox_options._options_pointer and \
'trace_enabled' in settings and settings['trace_enabled']:
ts.vAddLoggerCallback(tox_options)
LOG_INFO("c-toxcore trace_enabled enabled" )
else:
LOG_WARN("No tox_options._options_pointer to add self_logger_cb" )

View file

@ -1,7 +1,10 @@
import os.path
import utils.util
import wave
import pyaudio
import os.path
import tox_wrapper.tests.support_testing as ts
with ts.ignoreStderr():
import pyaudio
global LOG
import logging

View file

@ -1756,7 +1756,7 @@ class ToxSuite(unittest.TestCase):
else:
LOG.info("passed test_tox_savedata")
def vOargsToxPreamble(oArgs, Tox, ToxTest):
def vOargsToxPreamble(oArgs, Tox, ToxTest) -> None:
ts.vSetupLogging(oArgs)

View file

@ -1,12 +1,13 @@
import threading
from PyQt5 import QtCore, QtGui, QtWidgets
import pyaudio
import wave
from ui import widgets
import utils.util as util
import tox_wrapper.tests.support_testing as ts
with ts.ignoreStderr():
import pyaudio
global LOG
import logging

View file

@ -1,6 +1,6 @@
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets, uic
import pyaudio
from user_data.settings import *
from utils.util import *
@ -8,6 +8,8 @@ from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow
import updater.updater as updater
import utils.ui as util_ui
import tox_wrapper.tests.support_testing as ts
with ts.ignoreStderr():
import pyaudio
from user_data import settings
global LOG

View file

@ -17,7 +17,6 @@ LOG = logging.getLogger('settings')
def merge_args_into_settings(args, settings):
if args:
print(repr(args.__dict__.keys()))
if not hasattr(args, 'audio'):
LOG.warn('No audio ' +repr(args))
settings['audio'] = getattr(args, 'audio')
@ -131,17 +130,17 @@ class Settings(dict):
Settings of current profile + global app settings
"""
def __init__(self, toxes, path, app):
self._path = path
self._profile_path = path.replace('.json', '.tox')
def __init__(self, toxes, json_path, app):
self._toxes = toxes
self._app = app
self._path = app._path
self._args = app._args
self._oArgs = app._args
self._log = lambda l: LOG.log(self._oArgs.loglevel, l)
self._profile_path = app._path # json_path.replace('.json', '.tox')
self._settings_saved_event = Event()
path = json_path.replace('.tox', '.json')
if path and os.path.isfile(path):
try:
with open(path, 'rb') as fl:
@ -151,9 +150,9 @@ class Settings(dict):
info = json.loads(str(data, 'utf-8'))
LOG.debug('Parsed settings from: ' + str(path))
except Exception as ex:
title = 'Error opening/parsing settings file: '
text = title + path
LOG.error(title +str(ex))
title = f"Error opening/parsing settings file:"
text = title +f"\n{path}\n"
LOG.error(text +str(ex))
util_ui.message_box(text, title)
info = Settings.get_default_settings(app._args)
user_data.settings.clean_settings(info)
@ -161,7 +160,7 @@ class Settings(dict):
LOG.debug('get_default_settings for: ' + repr(path))
info = Settings.get_default_settings(app._args)
if not os.path.exists(path):
if not path or not os.path.exists(path):
merge_args_into_settings(app._args, info)
else:
aC = self._changed(app._args, info)
@ -178,12 +177,16 @@ class Settings(dict):
merge_args_into_settings(app._args, info)
info['audio'] = getattr(app._args, 'audio')
info['video'] = getattr(app._args, 'video')
info['trace_enabled'] = getattr(app._args, 'trace_enabled')
if getattr(app._args, 'trace_enabled'):
info['trace_enabled'] = getattr(app._args, 'trace_enabled')
else:
LOG.warn("app._args, 'trace_enabled")
info['trace_enabled'] = False
super().__init__(info)
self._upgrade()
LOG.info('Parsed settings from: ' + str(path))
ex = f"self=id(self) {self!r}"
ex = f"self=id(self) {self}"
LOG.debug(ex)
self.save()
@ -206,10 +209,16 @@ class Settings(dict):
text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8')))
else:
text = bytes(text, 'utf-8')
if not self._path:
#?
self._path = os.path.join(get_user_config_path(), 'toxygen.json')
tmp = self._path + str(os.getpid())
try:
with open(tmp, 'wb') as fl:
fl.write(text)
if os.path.exists(self._path+'.bak'):
os.remove(self._path+'.bak')
os.rename(self._path, self._path+'.bak')
os.rename(tmp, self._path)
except Exception as e:
LOG.warn(f'Error saving to {self._path} ' +str(e))
@ -221,17 +230,17 @@ class Settings(dict):
if os.path.isfile(path):
os.remove(path)
def set_active_profile(self):
def set_active_profile(self, profile_path):
"""
Mark current profile as active
"""
path = self._profile_path + '.lock'
path = profile_path + '.lock'
try:
import shutil
except:
pass
else:
shutil.copy2(self._profile_path, path)
shutil.copy2(profile_path, path)
# need to open this with the same perms as _profile_path
# copy profile_path and then write?
with open(path, 'wb') as fl:
@ -250,9 +259,9 @@ class Settings(dict):
# Static methods
@staticmethod
def get_auto_profile():
def get_auto_profile(appdir):
# self._path =
p = os.path.join(os.path.dirname(self._app._path), 'toxygen.json')
p = os.path.join(appdir, 'toxygen.json')
if not os.path.isfile(p):
return None
with open(p) as fl:
@ -260,14 +269,15 @@ class Settings(dict):
try:
auto = json.loads(data)
except Exception as ex:
LOG.warn(f"json.loads {data}: {ex!s}")
LOG.warn(f"json.loads {data}: {ex}")
auto = {}
if 'profile_path' in auto:
path = str(auto['profile_path'])
if not os.path.isabs(path):
path = join_path(path, curr_directory(__file__))
path = join_path(path, os.path.dirname(os.path.realpath(__file__)))
if os.path.isfile(path):
return path
return None
@staticmethod
def supported_languages():
@ -276,7 +286,7 @@ class Settings(dict):
@staticmethod
def set_auto_profile(path):
p = os.path.join(os.path.dirname(self._app._path), 'toxygen.json')
p = os.path.join(os.path.dirname(path), 'toxygen.json')
if os.path.isfile(p):
with open(p) as fl:
data = fl.read()
@ -289,7 +299,7 @@ class Settings(dict):
@staticmethod
def reset_auto_profile():
p = os.path.join(os.path.dirname(self._app._path), 'toxygen.json')
p = os.path.join(os.path.dirname(app._path), 'toxygen.json')
if os.path.isfile(p):
with open(p) as fl:
data = fl.read()