fixed json overwrite
This commit is contained in:
parent
e778108834
commit
c70c501fdd
12 changed files with 283 additions and 25 deletions
|
@ -38,6 +38,7 @@ written in pure Python3.
|
|||
- Changing nospam
|
||||
- File resuming
|
||||
- Read receipts
|
||||
- uses gevent
|
||||
- NGC groups
|
||||
|
||||
### Screenshots
|
||||
|
@ -82,6 +83,7 @@ Weechat has a Jabber plugin to enable XMPP:
|
|||
/help jabber
|
||||
```
|
||||
so you can have Tox, IRC and XMPP in the same application!
|
||||
See docs/ToxygenWeechat.md
|
||||
|
||||
## Install
|
||||
|
||||
|
|
5
ToDo.md
5
ToDo.md
|
@ -55,8 +55,11 @@ line.
|
|||
|
||||
Migrate PyQt5 to qtpy - almost done.
|
||||
|
||||
Maybe migrate gevent to asyncio, or look at https://pypi.org/project/asyncio-gevent/
|
||||
Maybe migrate gevent to asyncio, and migrate to
|
||||
[qasync](https://github.com/CabbageDevelopment/qasync)
|
||||
(see https://git.plastiras.org/emdee/phantompy ).
|
||||
|
||||
(Also look at https://pypi.org/project/asyncio-gevent/ but it's dead).
|
||||
|
||||
## Standards
|
||||
|
||||
|
|
153
docs/ToxygenWeechat.md
Normal file
153
docs/ToxygenWeechat.md
Normal file
|
@ -0,0 +1,153 @@
|
|||
## Toxygen Weechat
|
||||
|
||||
You can have a [weechat](https://github.com/weechat/qweechat)
|
||||
console so that you can have IRC and jabber in a window as well as Tox.
|
||||
There's a copy of qweechat in ```thirdparty/qweechat``` backported to
|
||||
PyQt5 and integrated into toxygen. Follow the normal instructions for
|
||||
adding a ```relay``` to [weechat](https://github.com/weechat/weechat)
|
||||
```
|
||||
/relay add ipv4.ssl.weechat 9001
|
||||
/relay start ipv4.ssl.weechat
|
||||
```
|
||||
or
|
||||
```
|
||||
/set relay.network.ipv6 off
|
||||
/set relay.network.password password
|
||||
/relay add weechat 9000
|
||||
/relay start weechat
|
||||
```
|
||||
and use the Plugins/Weechat Console to start weechat under Toxygen.
|
||||
Then use the File/Connect menu item of the Console to connect to weechat.
|
||||
|
||||
Weechat has a Jabber plugin to enable XMPP:
|
||||
```
|
||||
/python load jabber.el
|
||||
/help jabber
|
||||
```
|
||||
so you can have Tox, IRC and XMPP in the same application!
|
||||
|
||||
### Creating servers for IRC over Tor
|
||||
|
||||
Create a proxy called tor
|
||||
```
|
||||
/proxy add tor socks5 127.0.0.1 9050
|
||||
```
|
||||
|
||||
It should now show up in the list of proxies.
|
||||
```
|
||||
/proxy list
|
||||
```
|
||||
|
||||
```
|
||||
/nick SyniTox
|
||||
```
|
||||
|
||||
## TLS certificates
|
||||
|
||||
[Create a Self-signed Certificate](https://www.oftc.net/NickServ/CertFP/)
|
||||
|
||||
Choose a SyniTox you will identify as.
|
||||
|
||||
Create a directory for your certificates ~/.config/weechat/ssl/
|
||||
and make a subdirectory for each server ~/.config/weechat/ssl/irc.oftc.net/
|
||||
|
||||
Change to the server directory and use openssl to make a keypair and answer the questions:
|
||||
```
|
||||
openssl req -nodes -newkey rsa:2048 -keyout SyniTox.key -x509 -days 3650 -out SyniTox.cer
|
||||
chmod 400 SyniTox.key
|
||||
```
|
||||
We now combine certificate and key to a single file SyniTox.pem
|
||||
```
|
||||
cat SyniTox.cer SyniTox.key > SyniTox.pem
|
||||
chmod 400 SyniTox.pem
|
||||
```
|
||||
|
||||
Do this for each server you want to connect to, or just use one for all of them.
|
||||
|
||||
### Libera TokTok channel
|
||||
|
||||
The main discussion forum for Tox is the #TokTok channel on libera.
|
||||
|
||||
libera has an onion server so we can map an address in tor. Add this
|
||||
to your /etc/tor/torrc
|
||||
```
|
||||
MapAddress palladium.libera.chat libera75jm6of4wxpxt4aynol3xjmbtxgfyjpu34ss4d7r7q2v5zrpyd.onion
|
||||
```
|
||||
|
||||
Define the server in weechat
|
||||
https://www.weechat.org/files/doc/stable/weechat_user.en.html#irc_sasl_authentication
|
||||
```
|
||||
/server remove libera
|
||||
/server add libera palladium.libera.chat/6697 -tls -tls_verify
|
||||
/set irc.server.libera.ipv6 off
|
||||
/set irc.server.libera.proxy tor
|
||||
/set irc.server.libera.username SyniTox
|
||||
/set irc.server.libera.nicks SyniTox
|
||||
/set irc.server.libera.tls on
|
||||
/set irc.server.libera.tls_cert "${weechat_config_dir}/ssl/libera.chat/SyniTox.pem"
|
||||
```
|
||||
|
||||
```
|
||||
/set irc.server.libera.sasl_mechanism ecdsa-nist256p-challenge
|
||||
/set irc.server.libera.sasl_username "SyniTox"
|
||||
/set irc.server.libera.sasl_key "${weechat_config_dir}/ssl/libera.chat/SyniTox.pem"
|
||||
```
|
||||
|
||||
Disconnect and connect back to the server.
|
||||
```
|
||||
/disconnect libera
|
||||
/connect libera
|
||||
```
|
||||
|
||||
/msg nickserv identify password SyniTox
|
||||
|
||||
|
||||
### oftc.net
|
||||
|
||||
To use oftc.net over tor, you need to authenticate by SSL certificates.
|
||||
|
||||
|
||||
Define the server in weechat
|
||||
```
|
||||
/server remove irc.oftc.net
|
||||
/server add OFTC irc.oftc.net/6697 -tls -tls_verify
|
||||
/set irc.server.OFTC.ipv6 off
|
||||
/set irc.server.OFTC.proxy tor
|
||||
/set irc.server.OFTC.username SyniTox
|
||||
/set irc.server.OFTC.nicks SyniTox
|
||||
/set irc.server.OFTC.tls on
|
||||
/set irc.server.OFTC.tls_cert "${weechat_config_dir}/ssl/irc.oftc.chat/SyniTox.pem"
|
||||
|
||||
# Disconnect and connect back to the server.
|
||||
/disconnect OFTC
|
||||
/connect OFTC
|
||||
```
|
||||
You must be identified in order to validate using certs
|
||||
```
|
||||
/msg nickserv identify password SyniTox
|
||||
```
|
||||
To allow NickServ to identify you based on this certificate you need
|
||||
to associate the certificate fingerprint with your nick. To do this
|
||||
issue the command cert add to Nickserv (try /msg nickserv helpcert).
|
||||
```
|
||||
/msg nickserv cert add
|
||||
```
|
||||
|
||||
### Privacy
|
||||
|
||||
[Add somes settings bellow to weechat](https://szorfein.github.io/weechat/tor/configure-weechat/).
|
||||
Detail from [faq](https://weechat.org/files/doc/weechat_faq.en.html#security).
|
||||
|
||||
```
|
||||
/set irc.server_default.msg_part ""
|
||||
/set irc.server_default.msg_quit ""
|
||||
/set irc.ctcp.clientinfo ""
|
||||
/set irc.ctcp.finger ""
|
||||
/set irc.ctcp.source ""
|
||||
/set irc.ctcp.time ""
|
||||
/set irc.ctcp.userinfo ""
|
||||
/set irc.ctcp.version ""
|
||||
/set irc.ctcp.ping ""
|
||||
/plugin unload xfer
|
||||
/set weechat.plugin.autoload "*,!xfer"
|
||||
```
|
67
docs/todo.md
Normal file
67
docs/todo.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Toxygen ToDo List
|
||||
|
||||
## Bugs
|
||||
|
||||
1. There is an agravating bug where new messages are not put in the
|
||||
current window, and a messages waiting indicator appears. You have
|
||||
to focus out of the window and then back in the window. this may be
|
||||
fixed already
|
||||
|
||||
2. The tray icon is flaky and has been disabled - look in app.py
|
||||
for bSHOW_TRAY
|
||||
|
||||
## Fix history
|
||||
|
||||
## Fix Audio
|
||||
|
||||
The code is in there but it's not working. It looks like audio input
|
||||
is working but not output. The code is all in there; I may have broken
|
||||
it trying to wire up the ability to set the audio device from the
|
||||
command line.
|
||||
|
||||
## Fix Video
|
||||
|
||||
The code is in there but it's not working. I may have broken it
|
||||
trying to wire up the ability to set the video device from the command
|
||||
line.
|
||||
|
||||
## NGC Groups
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Plugin system
|
||||
|
||||
1. Needs better documentation and checking.
|
||||
|
||||
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?
|
||||
|
||||
4. There needs to be a uniform way for plugins to wire into callbacks.
|
||||
|
||||
## check toxygen_wrapper
|
||||
|
||||
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.
|
||||
|
||||
2. https://git.plastiras.org/emdee/toxygen_wrapper needs packaging
|
||||
and making a dependency.
|
||||
|
||||
## Migration
|
||||
|
||||
Migrate PyQt5 to qtpy - almost done.
|
||||
|
||||
Maybe migrate gevent to asyncio, and migrate to
|
||||
[qasync](https://github.com/CabbageDevelopment/qasync)
|
||||
(see https://git.plastiras.org/emdee/phantompy ).
|
||||
|
||||
(Also look at https://pypi.org/project/asyncio-gevent/ but it's dead).
|
||||
|
||||
## Standards
|
||||
|
||||
There's a standard for Tox clients that this has not been tested against:
|
||||
https://tox.gitbooks.io/tox-client-standard/content/general_requirements/general_requirements.html
|
|
@ -6,6 +6,8 @@ import logging
|
|||
import signal
|
||||
import time
|
||||
|
||||
from gevent import monkey; monkey.patch_all(); del monkey # noqa
|
||||
|
||||
import faulthandler
|
||||
faulthandler.enable()
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import threading
|
|||
from time import sleep, time
|
||||
from copy import deepcopy
|
||||
|
||||
from gevent import monkey; monkey.patch_all(); del monkey # noqa
|
||||
import gevent
|
||||
|
||||
from qtpy import QtWidgets, QtGui, QtCore
|
||||
|
@ -182,6 +181,7 @@ class App:
|
|||
if uri is not None and uri.startswith('tox:'):
|
||||
self._uri = uri[4:]
|
||||
self._history = None
|
||||
self.bAppExiting = False
|
||||
|
||||
# Public methods
|
||||
|
||||
|
@ -296,7 +296,7 @@ class App:
|
|||
def _stop_app(self) -> None:
|
||||
LOG.debug("_stop_app")
|
||||
self._save_profile()
|
||||
#? self._history.save_history()
|
||||
self._history.save_history()
|
||||
|
||||
self._plugin_loader.stop()
|
||||
try:
|
||||
|
@ -304,10 +304,16 @@ class App:
|
|||
except (Exception, RuntimeError):
|
||||
# RuntimeError: cannot join current thread
|
||||
pass
|
||||
# I think there are threads still running here leading to a SEGV
|
||||
# File "/usr/lib/python3.11/threading.py", line 1401 in run
|
||||
# File "/usr/lib/python3.11/threading.py", line 1045 in _bootstrap_inner
|
||||
# File "/usr/lib/python3.11/threading.py", line 1002 in _bootstrap
|
||||
|
||||
if hasattr(self, '_tray') and self._tray:
|
||||
self._tray.hide()
|
||||
self._settings.close()
|
||||
|
||||
self.bAppExiting = True
|
||||
LOG.debug(f"stop_app: Killing {self._tox}")
|
||||
self._kill_toxav()
|
||||
self._kill_tox()
|
||||
|
@ -522,18 +528,21 @@ class App:
|
|||
return ls.result
|
||||
|
||||
def _load_existing_profile(self, profile_path) -> None:
|
||||
profile_path = profile_path.replace('.json', '.tox')
|
||||
LOG.info("_load_existing_profile " +repr(profile_path))
|
||||
assert os.path.exists(profile_path), profile_path
|
||||
self._profile_manager = ProfileManager(self._toxes, profile_path)
|
||||
self._profile_manager = ProfileManager(self._toxes, profile_path, app=self)
|
||||
data = self._profile_manager.open_profile()
|
||||
if self._toxes.is_data_encrypted(data):
|
||||
LOG.debug("_entering password")
|
||||
data = self._enter_password(data)
|
||||
LOG.debug("_entered password")
|
||||
json_file = profile_path.replace('.tox', '.json')
|
||||
assert os.path.exists(json_file), json_file
|
||||
if os.path.exists(json_file):
|
||||
LOG.debug("creating _settings from: " +json_file)
|
||||
self._settings = Settings(self._toxes, json_file, self)
|
||||
else:
|
||||
|
||||
self._tox = self._create_tox(data, self._settings)
|
||||
LOG.debug("created _tox")
|
||||
|
||||
|
@ -683,7 +692,8 @@ class App:
|
|||
self._contacts_provider = ContactProvider(self._tox,
|
||||
self._friend_factory,
|
||||
self._group_factory,
|
||||
self._group_peer_factory)
|
||||
self._group_peer_factory,
|
||||
app=self)
|
||||
self._profile = Profile(self._profile_manager,
|
||||
self._tox,
|
||||
self._ms,
|
||||
|
|
|
@ -11,12 +11,13 @@ from av.calls import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
|
|||
|
||||
class ContactProvider(tox_save.ToxSave):
|
||||
|
||||
def __init__(self, tox, friend_factory, group_factory, group_peer_factory):
|
||||
def __init__(self, tox, friend_factory, group_factory, group_peer_factory, app=None):
|
||||
super().__init__(tox)
|
||||
self._friend_factory = friend_factory
|
||||
self._group_factory = group_factory
|
||||
self._group_peer_factory = group_peer_factory
|
||||
self._cache = {} # key - contact's public key, value - contact instance
|
||||
self._app = app
|
||||
|
||||
# Friends
|
||||
|
||||
|
@ -41,6 +42,8 @@ class ContactProvider(tox_save.ToxSave):
|
|||
return friend
|
||||
|
||||
def get_all_friends(self):
|
||||
if self._app and self._app.bAppExiting:
|
||||
return
|
||||
try:
|
||||
friend_numbers = self._tox.self_get_friend_list()
|
||||
except Exception as e:
|
||||
|
|
|
@ -6,6 +6,7 @@ import common.tox_save as tox_save
|
|||
from middleware.threads import invoke_in_main_thread
|
||||
|
||||
iUMAXINT = 4294967295
|
||||
iRECONNECT = 50
|
||||
|
||||
global LOG
|
||||
import logging
|
||||
|
@ -15,7 +16,7 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave):
|
|||
"""
|
||||
Profile of current toxygen user.
|
||||
"""
|
||||
def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action):
|
||||
def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action, app=None):
|
||||
"""
|
||||
:param tox: tox instance
|
||||
:param screen: ref to main screen
|
||||
|
@ -34,32 +35,33 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave):
|
|||
self._reset_action = reset_action
|
||||
self._waiting_for_reconnection = False
|
||||
self._timer = None
|
||||
self._app = app
|
||||
|
||||
# Edit current user's data
|
||||
|
||||
def change_status(self):
|
||||
def change_status(self) -> None:
|
||||
"""
|
||||
Changes status of user (online, away, busy)
|
||||
"""
|
||||
if self._status is not None:
|
||||
self.set_status((self._status + 1) % 3)
|
||||
|
||||
def set_status(self, status):
|
||||
def set_status(self, status) -> None:
|
||||
super().set_status(status)
|
||||
if status is not None:
|
||||
self._tox.self_set_status(status)
|
||||
elif not self._waiting_for_reconnection:
|
||||
self._waiting_for_reconnection = True
|
||||
self._timer = threading.Timer(50, self._reconnect)
|
||||
self._timer = threading.Timer(iRECONNECT, self._reconnect)
|
||||
self._timer.start()
|
||||
|
||||
def set_name(self, value):
|
||||
def set_name(self, value) -> None:
|
||||
if self.name == value:
|
||||
return
|
||||
super().set_name(value)
|
||||
self._tox.self_set_name(self._name)
|
||||
|
||||
def set_status_message(self, value):
|
||||
def set_status_message(self, value) -> None:
|
||||
super().set_status_message(value)
|
||||
self._tox.self_set_status_message(self._status_message)
|
||||
|
||||
|
@ -72,19 +74,34 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave):
|
|||
|
||||
# Reset
|
||||
|
||||
def restart(self):
|
||||
def restart(self) -> None:
|
||||
"""
|
||||
Recreate tox instance
|
||||
"""
|
||||
self.status = None
|
||||
invoke_in_main_thread(self._reset_action)
|
||||
|
||||
def _reconnect(self):
|
||||
def _reconnect(self) -> None:
|
||||
self._waiting_for_reconnection = False
|
||||
if self._app and self._app.bAppExiting:
|
||||
# dont do anything after the app has been shipped
|
||||
# there's a segv that results
|
||||
return
|
||||
contacts = self._contacts_provider.get_all_friends()
|
||||
all_friends_offline = all(list(map(lambda x: x.status is None, contacts)))
|
||||
if self.status is None or (all_friends_offline and len(contacts)):
|
||||
self._waiting_for_reconnection = True
|
||||
self.restart()
|
||||
self._timer = threading.Timer(50, self._reconnect)
|
||||
self._timer = threading.Timer(iRECONNECT, self._reconnect)
|
||||
self._timer.start()
|
||||
|
||||
# Current thread 0x00007901a13ccb80 (most recent call first):
|
||||
# File "/usr/local/lib/python3.11/site-packages/tox_wrapper/tox.py", line 826 in self_get_friend_list_size
|
||||
# File "/usr/local/lib/python3.11/site-packages/tox_wrapper/tox.py", line 838 in self_get_friend_list
|
||||
# File "/mnt/o/var/local/src/toxygen/toxygen/contacts/contact_provider.py", line 45 in get_all_friends
|
||||
# File "/mnt/o/var/local/src/toxygen/toxygen/contacts/profile.py", line 90 in _reconnect
|
||||
# File "/usr/lib/python3.11/threading.py", line 1401 in run
|
||||
# File "/usr/lib/python3.11/threading.py", line 1045 in _bootstrap_inner
|
||||
# File "/usr/lib/python3.11/threading.py", line 1002 in _bootstrap
|
||||
#
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,15 +1,15 @@
|
|||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||
from qtpy import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
import tox_wrapper.tests.support_testing as ts
|
||||
with ts.ignoreStderr(): # not out
|
||||
import pyaudio
|
||||
|
||||
from user_data.settings import *
|
||||
from utils.util import *
|
||||
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
|
||||
|
|
|
@ -15,11 +15,12 @@ class ProfileManager:
|
|||
"""
|
||||
Class with methods for search, load and save profiles
|
||||
"""
|
||||
def __init__(self, toxes, path):
|
||||
def __init__(self, toxes, path, app=None):
|
||||
assert path
|
||||
self._toxes = toxes
|
||||
self._path = path
|
||||
assert path
|
||||
self._app = app
|
||||
self._directory = os.path.dirname(path)
|
||||
self._profile_saved_event = Event()
|
||||
# create /avatars if not exists:
|
||||
|
|
|
@ -138,7 +138,7 @@ class Settings(dict):
|
|||
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._profile_path = json_path.replace('.json', '.tox')
|
||||
|
||||
self._settings_saved_event = Event()
|
||||
path = json_path.replace('.tox', '.json')
|
||||
|
|
Loading…
Reference in a new issue