Compare commits
No commits in common. "ba013b6a814ad6c1b43ae54b9304b535fe796481" and "dcde8e3d1eae43a878aad8df8cfff99cada85ac9" have entirely different histories.
ba013b6a81
...
dcde8e3d1e
3
.gitignore
vendored
3
.gitignore
vendored
@ -25,5 +25,4 @@ Toxygen.egg-info
|
|||||||
*.tox
|
*.tox
|
||||||
.cache
|
.cache
|
||||||
*.db
|
*.db
|
||||||
*~
|
|
||||||
Makefile
|
|
||||||
|
130
_Bugs/segv.err
130
_Bugs/segv.err
@ -1,130 +0,0 @@
|
|||||||
0
|
|
||||||
TRAC> network.c#1748:net_connect connecting socket 58 to 127.0.0.1:9050
|
|
||||||
TRAC> Messenger.c#2709:do_messenger Friend num in DHT 2 != friend num in msger 14
|
|
||||||
TRAC> Messenger.c#2723:do_messenger F[--: 0] D3385007C28852C5398393E3338E6AABE5F86EF249BF724E7404233207D4D927
|
|
||||||
TRAC> Messenger.c#2723:do_messenger F[--: 1] 98984E104B8A97CC43AF03A27BE159AC1F4CF35FADCC03D6CD5F8D67B5942A56
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 185.87.49.189:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 185.87.49.189:3389 (0: OK) | 010001b95731bd0d...3d
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 37.221.66.161:443 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 37.221.66.161:443 (0: OK) | 01000125dd42a101...bb
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 172.93.52.70:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 139.162.110.188:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 37.59.63.150:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 37.97.185.116:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 85.143.221.42:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 104.244.74.69:38445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 168.119.209.10:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 81.169.136.229:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 91.219.59.156:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 46.101.197.175:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 198.199.98.108:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 188.225.9.167:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 5.19.249.240:38296 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 3= 94.156.35.247:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 172.93.52.70:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 172.93.52.70:33445 (0: OK) | 010001ac5d344682...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 139.162.110.188:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 139.162.110.188:33445 (0: OK) | 0100018ba26ebc82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 37.59.63.150:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 37.59.63.150:33445 (0: OK) | 010001253b3f9682...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 130.133.110.14:33445 (0: OK) | 01000182856e0e82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 37.97.185.116:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 37.97.185.116:33445 (0: OK) | 0100012561b97482...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 85.143.221.42:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 85.143.221.42:33445 (0: OK) | 010001558fdd2a82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 104.244.74.69:38445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 104.244.74.69:38445 (0: OK) | 01000168f44a4596...2d
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 49.12.229.145:3389 (0: OK) | 010001310ce5910d...3d
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 168.119.209.10:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 168.119.209.10:33445 (0: OK) | 010001a877d10a82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 81.169.136.229:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 81.169.136.229:33445 (0: OK) | 01000151a988e582...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 91.219.59.156:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 91.219.59.156:33445 (0: OK) | 0100015bdb3b9c82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 46.101.197.175:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 46.101.197.175:3389 (0: OK) | 0100012e65c5af0d...3d
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 198.199.98.108:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 198.199.98.108:33445 (0: OK) | 010001c6c7626c82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 130.133.110.14:33445 (0: OK) | 01000182856e0e82...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 49.12.229.145:3389 (0: OK) | 010001310ce5910d...3d
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 188.225.9.167:33445 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 188.225.9.167:33445 (0: OK) | 010001bce109a782...a5
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 5.19.249.240:38296 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 5.19.249.240:38296 (0: OK) | 0100010513f9f095...98
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] =>T 2= 94.156.35.247:3389 (0: OK) | 0000000000000000...00
|
|
||||||
TRAC> network.c#789:loglogdata [05 = <unknown> ] T=> 10= 94.156.35.247:3389 (0: OK) | 0100015e9c23f70d...3d
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
|
|
||||||
app.contacts.contacts_manager INFO update_groups_numbers len(groups)={len(groups)}
|
|
||||||
|
|
||||||
Thread 76 "ToxIterateThrea" received signal SIGSEGV, Segmentation fault.
|
|
||||||
[Switching to Thread 0x7ffedcb6b640 (LWP 2950427)]
|
|
@ -1,11 +0,0 @@
|
|||||||
ping tox.abilinski.com
|
|
||||||
ping: socket: Address family not supported by protocol
|
|
||||||
PING tox.abilinski.com (172.103.226.229) 56(84) bytes of data.
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=1 ttl=48 time=86.6 ms
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=2 ttl=48 time=83.1 ms
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=3 ttl=48 time=82.9 ms
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=4 ttl=48 time=83.4 ms
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=5 ttl=48 time=102 ms
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=6 ttl=48 time=87.4 ms
|
|
||||||
64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=7 ttl=48 time=84.9 ms
|
|
||||||
^C
|
|
@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
ROLE=logging
|
|
||||||
/var/local/bin/pydev_pylint.bash -E -f text *py [a-nr-z]*/*py >.pylint.err
|
|
||||||
/var/local/bin/pydev_pylint.bash *py [a-nr-z]*/*py >.pylint.out
|
|
@ -179,7 +179,9 @@ class App:
|
|||||||
self._uri = uri[4:]
|
self._uri = uri[4:]
|
||||||
self._history = None
|
self._history = None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Public methods
|
# Public methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def set_trace(self):
|
def set_trace(self):
|
||||||
"""unused"""
|
"""unused"""
|
||||||
@ -253,7 +255,9 @@ class App:
|
|||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# App executing
|
# App executing
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _execute_app(self):
|
def _execute_app(self):
|
||||||
LOG.debug("_execute_app")
|
LOG.debug("_execute_app")
|
||||||
@ -317,7 +321,9 @@ class App:
|
|||||||
oArgs.log_oFd.close()
|
oArgs.log_oFd.close()
|
||||||
delattr(oArgs, 'log_oFd')
|
delattr(oArgs, 'log_oFd')
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# App loading
|
# App loading
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _load_base_style(self):
|
def _load_base_style(self):
|
||||||
if self._args.theme in ['', 'default']: return
|
if self._args.theme in ['', 'default']: return
|
||||||
@ -464,7 +470,9 @@ class App:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Threads
|
# Threads
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _start_threads(self, initial_start=True):
|
def _start_threads(self, initial_start=True):
|
||||||
LOG.debug(f"_start_threads before: {threading.enumerate()!r}")
|
LOG.debug(f"_start_threads before: {threading.enumerate()!r}")
|
||||||
@ -505,7 +513,9 @@ class App:
|
|||||||
self._tox.iterate()
|
self._tox.iterate()
|
||||||
gevent.sleep(interval / 1000.0)
|
gevent.sleep(interval / 1000.0)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Profiles
|
# Profiles
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _select_profile(self):
|
def _select_profile(self):
|
||||||
LOG.debug("_select_profile")
|
LOG.debug("_select_profile")
|
||||||
@ -590,7 +600,9 @@ class App:
|
|||||||
data = data or self._tox.get_savedata()
|
data = data or self._tox.get_savedata()
|
||||||
self._profile_manager.save_profile(data)
|
self._profile_manager.save_profile(data)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Other private methods
|
# Other private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _enter_password(self, data):
|
def _enter_password(self, data):
|
||||||
"""
|
"""
|
||||||
@ -955,18 +967,17 @@ class App:
|
|||||||
reply = util_ui.question(text, title)
|
reply = util_ui.question(text, title)
|
||||||
if not reply: return
|
if not reply: return
|
||||||
|
|
||||||
if self._args.proxy_type == 0:
|
|
||||||
sProt = "udp4"
|
|
||||||
else:
|
|
||||||
sProt = "tcp4"
|
|
||||||
if lElts is None:
|
if lElts is None:
|
||||||
if self._args.proxy_type == 0:
|
if self._args.proxy_type == 0:
|
||||||
lElts = self._settings['current_nodes_udp']
|
sProt = "udp4"
|
||||||
|
lElts = self._settings['current_nodes_tcp']
|
||||||
else:
|
else:
|
||||||
|
sProt = "tcp4"
|
||||||
lElts = self._settings['current_nodes_tcp']
|
lElts = self._settings['current_nodes_tcp']
|
||||||
shuffle(lElts)
|
shuffle(lElts)
|
||||||
try:
|
try:
|
||||||
ts.bootstrap_iNmapInfo(lElts, self._args, sProt)
|
ts.bootstrap_iNmapInfo(lElts, self._args, sProt)
|
||||||
|
self._ms.log_console()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(f"test_nmap ' +' : {e}")
|
LOG.error(f"test_nmap ' +' : {e}")
|
||||||
LOG.error('_test_nmap(): ' \
|
LOG.error('_test_nmap(): ' \
|
||||||
@ -979,7 +990,7 @@ class App:
|
|||||||
self._ms.log_console()
|
self._ms.log_console()
|
||||||
|
|
||||||
def _test_main(self):
|
def _test_main(self):
|
||||||
from toxygen_wrapper.wrapper_tests.tests_wrapper import main as tests_main
|
from tests.tests_socks import main as tests_main
|
||||||
LOG.debug("_test_main")
|
LOG.debug("_test_main")
|
||||||
if not self._tox: return
|
if not self._tox: return
|
||||||
title = 'Extended Test Suite'
|
title = 'Extended Test Suite'
|
||||||
|
@ -17,7 +17,9 @@ class Call:
|
|||||||
|
|
||||||
is_active = property(get_is_active, set_is_active)
|
is_active = property(get_is_active, set_is_active)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Audio
|
# Audio
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_in_audio(self):
|
def get_in_audio(self):
|
||||||
return self._in_audio
|
return self._in_audio
|
||||||
@ -35,7 +37,9 @@ class Call:
|
|||||||
|
|
||||||
out_audio = property(get_out_audio, set_out_audio)
|
out_audio = property(get_out_audio, set_out_audio)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Video
|
# Video
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_in_video(self):
|
def get_in_video(self):
|
||||||
return self._in_video
|
return self._in_video
|
||||||
|
@ -91,7 +91,9 @@ class AV(common.tox_save.ToxAvSave):
|
|||||||
def __contains__(self, friend_number):
|
def __contains__(self, friend_number):
|
||||||
return friend_number in self._calls
|
return friend_number in self._calls
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Calls
|
# Calls
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def __call__(self, friend_number, audio, video):
|
def __call__(self, friend_number, audio, video):
|
||||||
"""Call friend with specified number"""
|
"""Call friend with specified number"""
|
||||||
@ -105,7 +107,7 @@ class AV(common.tox_save.ToxAvSave):
|
|||||||
self._toxav.call(friend_number,
|
self._toxav.call(friend_number,
|
||||||
self._audio_krate_tox_audio if audio else 0,
|
self._audio_krate_tox_audio if audio else 0,
|
||||||
self._audio_krate_tox_video if video else 0)
|
self._audio_krate_tox_video if video else 0)
|
||||||
except Exception as e:
|
except ArgumentError as e:
|
||||||
LOG.warn(f"_toxav.call already has {friend_number}")
|
LOG.warn(f"_toxav.call already has {friend_number}")
|
||||||
return
|
return
|
||||||
self._calls[friend_number] = Call(audio, video)
|
self._calls[friend_number] = Call(audio, video)
|
||||||
@ -114,7 +116,7 @@ class AV(common.tox_save.ToxAvSave):
|
|||||||
|
|
||||||
def accept_call(self, friend_number, audio_enabled, video_enabled):
|
def accept_call(self, friend_number, audio_enabled, video_enabled):
|
||||||
# obsolete
|
# obsolete
|
||||||
return self.call_accept_call(friend_number, audio_enabled, video_enabled)
|
return call_accept_call(self, friend_number, audio_enabled, video_enabled)
|
||||||
|
|
||||||
def 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}" +
|
LOG.debug(f"call_accept_call from {friend_number} {self._running}" +
|
||||||
@ -187,7 +189,9 @@ class AV(common.tox_save.ToxAvSave):
|
|||||||
def is_video_call(self, number):
|
def is_video_call(self, number):
|
||||||
return number in self and self._calls[number].in_video
|
return number in self and self._calls[number].in_video
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Threads
|
# Threads
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def start_audio_thread(self):
|
def start_audio_thread(self):
|
||||||
"""
|
"""
|
||||||
@ -346,7 +350,9 @@ class AV(common.tox_save.ToxAvSave):
|
|||||||
self._video_thread = None
|
self._video_thread = None
|
||||||
self._video = None
|
self._video = None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Incoming chunks
|
# Incoming chunks
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def audio_chunk(self, samples, channels_count, rate):
|
def audio_chunk(self, samples, channels_count, rate):
|
||||||
"""
|
"""
|
||||||
@ -382,7 +388,9 @@ class AV(common.tox_save.ToxAvSave):
|
|||||||
LOG.debug(f"audio_chunk output_device_index={iOutput} rate={rate} channels={channels_count}")
|
LOG.debug(f"audio_chunk output_device_index={iOutput} rate={rate} channels={channels_count}")
|
||||||
self._out_stream.write(samples)
|
self._out_stream.write(samples)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# AV sending
|
# AV sending
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_audio_data(self, data, count, *largs, **kwargs):
|
def send_audio_data(self, data, count, *largs, **kwargs):
|
||||||
pcm = data
|
pcm = data
|
||||||
|
@ -30,7 +30,9 @@ class CallsManager:
|
|||||||
def set_toxav(self, toxav):
|
def set_toxav(self, toxav):
|
||||||
self._callav.set_toxav(toxav)
|
self._callav.set_toxav(toxav)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Events
|
# Events
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_call_started_event(self):
|
def get_call_started_event(self):
|
||||||
return self._call_started_event
|
return self._call_started_event
|
||||||
@ -42,7 +44,9 @@ class CallsManager:
|
|||||||
|
|
||||||
call_finished_event = property(get_call_finished_event)
|
call_finished_event = property(get_call_finished_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# AV support
|
# AV support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def call_click(self, audio=True, video=False):
|
def call_click(self, audio=True, video=False):
|
||||||
"""User clicked audio button in main window"""
|
"""User clicked audio button in main window"""
|
||||||
@ -155,7 +159,9 @@ class CallsManager:
|
|||||||
if friend_number in self._callav:
|
if friend_number in self._callav:
|
||||||
self._callav.finish_call(friend_number, True)
|
self._callav.finish_call(friend_number, True)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _get_incoming_call_widget(self, friend_number, text, friend_name):
|
def _get_incoming_call_widget(self, friend_number, text, friend_name):
|
||||||
return av_widgets.IncomingCallWidget(self._settings, self, friend_number, text, friend_name)
|
return av_widgets.IncomingCallWidget(self._settings, self, friend_number, text, friend_name)
|
||||||
|
@ -43,6 +43,6 @@ def download_nodes_list(settings, oArgs):
|
|||||||
def _save_nodes(nodes, app):
|
def _save_nodes(nodes, app):
|
||||||
if not nodes:
|
if not nodes:
|
||||||
return
|
return
|
||||||
with open(_get_nodes_path(app._args), 'wb') as fl:
|
with open(_get_nodes_path(oArgs=app._args), 'wb') as fl:
|
||||||
LOG.info("Saving nodes to " +_get_nodes_path(app._args))
|
LOG.info("Saving nodes to " +_get_nodes_path())
|
||||||
fl.write(nodes)
|
fl.write(nodes)
|
||||||
|
@ -35,7 +35,9 @@ class BaseContact:
|
|||||||
self._avatar_changed_event = event.Event()
|
self._avatar_changed_event = event.Event()
|
||||||
self.init_widget()
|
self.init_widget()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Name - current name or alias of user
|
# Name - current name or alias of user
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self._name
|
return self._name
|
||||||
@ -55,7 +57,9 @@ class BaseContact:
|
|||||||
|
|
||||||
name_changed_event = property(get_name_changed_event)
|
name_changed_event = property(get_name_changed_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Status message
|
# Status message
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_status_message(self):
|
def get_status_message(self):
|
||||||
return self._status_message
|
return self._status_message
|
||||||
@ -75,7 +79,9 @@ class BaseContact:
|
|||||||
|
|
||||||
status_message_changed_event = property(get_status_message_changed_event)
|
status_message_changed_event = property(get_status_message_changed_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Status
|
# Status
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
return self._status
|
return self._status
|
||||||
@ -94,30 +100,31 @@ class BaseContact:
|
|||||||
|
|
||||||
status_changed_event = property(get_status_changed_event)
|
status_changed_event = property(get_status_changed_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# TOX ID. WARNING: for friend it will return public key, for profile - full address
|
# TOX ID. WARNING: for friend it will return public key, for profile - full address
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_tox_id(self):
|
def get_tox_id(self):
|
||||||
return self._tox_id
|
return self._tox_id
|
||||||
|
|
||||||
tox_id = property(get_tox_id)
|
tox_id = property(get_tox_id)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Avatars
|
# Avatars
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def load_avatar(self):
|
def load_avatar(self):
|
||||||
"""
|
"""
|
||||||
Tries to load avatar of contact or uses default avatar
|
Tries to load avatar of contact or uses default avatar
|
||||||
"""
|
"""
|
||||||
try:
|
avatar_path = self.get_avatar_path()
|
||||||
avatar_path = self.get_avatar_path()
|
width = self._widget.avatar_label.width()
|
||||||
width = self._widget.avatar_label.width()
|
pixmap = QtGui.QPixmap(avatar_path)
|
||||||
pixmap = QtGui.QPixmap(avatar_path)
|
self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio,
|
||||||
self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio,
|
QtCore.Qt.SmoothTransformation))
|
||||||
QtCore.Qt.SmoothTransformation))
|
self._widget.avatar_label.repaint()
|
||||||
self._widget.avatar_label.repaint()
|
self._avatar_changed_event(avatar_path)
|
||||||
self._avatar_changed_event(avatar_path)
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def reset_avatar(self, generate_new):
|
def reset_avatar(self, generate_new):
|
||||||
avatar_path = self.get_avatar_path()
|
avatar_path = self.get_avatar_path()
|
||||||
if os.path.isfile(avatar_path) and not avatar_path == self._get_default_avatar_path():
|
if os.path.isfile(avatar_path) and not avatar_path == self._get_default_avatar_path():
|
||||||
@ -158,7 +165,9 @@ class BaseContact:
|
|||||||
|
|
||||||
avatar_changed_event = property(get_avatar_changed_event)
|
avatar_changed_event = property(get_avatar_changed_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Widgets
|
# Widgets
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def init_widget(self):
|
def init_widget(self):
|
||||||
self._widget.name.setText(self._name)
|
self._widget.name.setText(self._name)
|
||||||
@ -168,7 +177,9 @@ class BaseContact:
|
|||||||
self._widget.connection_status.update(self._status)
|
self._widget.connection_status.update(self._status)
|
||||||
self.load_avatar()
|
self.load_avatar()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_default_avatar_path():
|
def _get_default_avatar_path():
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
from pydenticon import Generator
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
from pydenticon import Generator
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Typing notifications
|
# Typing notifications
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class BaseTypingNotificationHandler:
|
class BaseTypingNotificationHandler:
|
||||||
|
|
||||||
@ -28,7 +30,9 @@ class FriendTypingNotificationHandler(BaseTypingNotificationHandler):
|
|||||||
BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler()
|
BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler()
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Identicons support
|
# Identicons support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def generate_avatar(public_key):
|
def generate_avatar(public_key):
|
||||||
|
@ -42,7 +42,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
if hasattr(self, '_message_getter'):
|
if hasattr(self, '_message_getter'):
|
||||||
del self._message_getter
|
del self._message_getter
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# History support
|
# History support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def load_corr(self, first_time=True):
|
def load_corr(self, first_time=True):
|
||||||
"""
|
"""
|
||||||
@ -119,7 +121,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
|
|
||||||
return TextMessage(message, author, unix_time, message_type, unique_id)
|
return TextMessage(message, author, unix_time, message_type, unique_id)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Unsent messages
|
# Unsent messages
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_unsent_messages(self):
|
def get_unsent_messages(self):
|
||||||
"""
|
"""
|
||||||
@ -132,11 +136,10 @@ class Contact(basecontact.BaseContact):
|
|||||||
"""
|
"""
|
||||||
:return list of unsent messages for saving
|
:return list of unsent messages for saving
|
||||||
"""
|
"""
|
||||||
# and m.tox_message_id == tox_message_id,
|
message = list(filter(lambda m: m.author is not None
|
||||||
messages = filter(lambda m: m.author is not None
|
|
||||||
and m.author.type == MESSAGE_AUTHOR['NOT_SENT']
|
and m.author.type == MESSAGE_AUTHOR['NOT_SENT']
|
||||||
self._corr)
|
and m.tox_message_id == tox_message_id,
|
||||||
# was message = list(...)[0]
|
self._corr))[0]
|
||||||
return list(messages)
|
return list(messages)
|
||||||
|
|
||||||
def mark_as_sent(self, tox_message_id):
|
def mark_as_sent(self, tox_message_id):
|
||||||
@ -148,7 +151,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
# wrapped C/C++ object of type QLabel has been deleted
|
# wrapped C/C++ object of type QLabel has been deleted
|
||||||
LOG.error(f"Mark as sent: {ex!s}")
|
LOG.error(f"Mark as sent: {ex!s}")
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Message deletion
|
# Message deletion
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def delete_message(self, message_id):
|
def delete_message(self, message_id):
|
||||||
elem = list(filter(lambda m: m.message_id == message_id, self._corr))[0]
|
elem = list(filter(lambda m: m.message_id == message_id, self._corr))[0]
|
||||||
@ -194,7 +199,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
self._corr))
|
self._corr))
|
||||||
self._unsaved_messages = len(self.get_unsent_messages())
|
self._unsaved_messages = len(self.get_unsent_messages())
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Chat history search
|
# Chat history search
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def search_string(self, search_string):
|
def search_string(self, search_string):
|
||||||
self._search_string, self._search_index = search_string, 0
|
self._search_string, self._search_index = search_string, 0
|
||||||
@ -227,7 +234,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
return i
|
return i
|
||||||
return None # not found
|
return None # not found
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Current text - text from message area
|
# Current text - text from message area
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_curr_text(self):
|
def get_curr_text(self):
|
||||||
return self._curr_text
|
return self._curr_text
|
||||||
@ -237,7 +246,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
|
|
||||||
curr_text = property(get_curr_text, set_curr_text)
|
curr_text = property(get_curr_text, set_curr_text)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Alias support
|
# Alias support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def set_name(self, value):
|
def set_name(self, value):
|
||||||
"""
|
"""
|
||||||
@ -253,7 +264,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
def has_alias(self):
|
def has_alias(self):
|
||||||
return self._alias
|
return self._alias
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Visibility in friends' list
|
# Visibility in friends' list
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_visibility(self):
|
def get_visibility(self):
|
||||||
return self._visible
|
return self._visible
|
||||||
@ -263,7 +276,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
|
|
||||||
visibility = property(get_visibility, set_visibility)
|
visibility = property(get_visibility, set_visibility)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Unread messages and other actions from friend
|
# Unread messages and other actions from friend
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_actions(self):
|
def get_actions(self):
|
||||||
return self._new_actions
|
return self._new_actions
|
||||||
@ -291,7 +306,9 @@ class Contact(basecontact.BaseContact):
|
|||||||
|
|
||||||
messages = property(get_messages)
|
messages = property(get_messages)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Friend's or group's number (can be used in toxcore)
|
# Friend's or group's number (can be used in toxcore)
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_number(self):
|
def get_number(self):
|
||||||
return self._number
|
return self._number
|
||||||
@ -301,19 +318,25 @@ class Contact(basecontact.BaseContact):
|
|||||||
|
|
||||||
number = property(get_number, set_number)
|
number = property(get_number, set_number)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Typing notifications
|
# Typing notifications
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_typing_notification_handler(self):
|
def get_typing_notification_handler(self):
|
||||||
return common.BaseTypingNotificationHandler.DEFAULT_HANDLER
|
return common.BaseTypingNotificationHandler.DEFAULT_HANDLER
|
||||||
|
|
||||||
typing_notification_handler = property(get_typing_notification_handler)
|
typing_notification_handler = property(get_typing_notification_handler)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Context menu support
|
# Context menu support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_context_menu_generator(self):
|
def get_context_menu_generator(self):
|
||||||
return BaseContactMenuGenerator(self)
|
return BaseContactMenuGenerator(self)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Filtration support
|
# Filtration support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def set_widget(self, widget):
|
def set_widget(self, widget):
|
||||||
self._widget = widget
|
self._widget = widget
|
||||||
|
@ -8,7 +8,9 @@ global LOG
|
|||||||
import logging
|
import logging
|
||||||
LOG = logging.getLogger('app')
|
LOG = logging.getLogger('app')
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Builder
|
# Builder
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _create_menu(menu_name, parent):
|
def _create_menu(menu_name, parent):
|
||||||
menu_name = menu_name or ''
|
menu_name = menu_name or ''
|
||||||
@ -81,7 +83,9 @@ class ContactMenuBuilder:
|
|||||||
self._actions[self._index] = (text, handler)
|
self._actions[self._index] = (text, handler)
|
||||||
self._index += 1
|
self._index += 1
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Generators
|
# Generators
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class BaseContactMenuGenerator:
|
class BaseContactMenuGenerator:
|
||||||
@ -92,7 +96,9 @@ class BaseContactMenuGenerator:
|
|||||||
def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader):
|
def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader):
|
||||||
return ContactMenuBuilder().build()
|
return ContactMenuBuilder().build()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _generate_copy_menu_builder(self, main_screen):
|
def _generate_copy_menu_builder(self, main_screen):
|
||||||
copy_menu_builder = ContactMenuBuilder()
|
copy_menu_builder = ContactMenuBuilder()
|
||||||
@ -144,7 +150,9 @@ class FriendMenuGenerator(BaseContactMenuGenerator):
|
|||||||
|
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_plugins_menu_builder(plugin_loader, number):
|
def _generate_plugins_menu_builder(plugin_loader, number):
|
||||||
|
@ -28,13 +28,15 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
self._group_peer_factory = group_peer_factory
|
self._group_peer_factory = group_peer_factory
|
||||||
self._cache = {} # key - contact's public key, value - contact instance
|
self._cache = {} # key - contact's public key, value - contact instance
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Friends
|
# Friends
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_friend_by_number(self, friend_number):
|
def get_friend_by_number(self, friend_number):
|
||||||
try:
|
try:
|
||||||
public_key = self._tox.friend_get_public_key(friend_number)
|
public_key = self._tox.friend_get_public_key(friend_number)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG_WARN(f"CP.get_friend_by_number NO {friend_number} {e} ")
|
LOG_WARN(f"get_friend_by_number NO {friend_number} {e} ")
|
||||||
return None
|
return None
|
||||||
return self.get_friend_by_public_key(public_key)
|
return self.get_friend_by_public_key(public_key)
|
||||||
|
|
||||||
@ -44,7 +46,7 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
return friend
|
return friend
|
||||||
friend = self._friend_factory.create_friend_by_public_key(public_key)
|
friend = self._friend_factory.create_friend_by_public_key(public_key)
|
||||||
self._add_to_cache(public_key, friend)
|
self._add_to_cache(public_key, friend)
|
||||||
LOG_INFO(f"CP.get_friend_by_public_key ADDED {friend} ")
|
LOG_INFO(f"get_friend_by_public_key ADDED {friend} ")
|
||||||
|
|
||||||
return friend
|
return friend
|
||||||
|
|
||||||
@ -52,13 +54,15 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
try:
|
try:
|
||||||
friend_numbers = self._tox.self_get_friend_list()
|
friend_numbers = self._tox.self_get_friend_list()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG_WARN(f"CP.get_all_friends NO {friend_numbers} {e} ")
|
LOG_WARN(f"get_all_friends NO {friend_numbers} {e} ")
|
||||||
return None
|
return None
|
||||||
friends = map(lambda n: self.get_friend_by_number(n), friend_numbers)
|
friends = map(lambda n: self.get_friend_by_number(n), friend_numbers)
|
||||||
|
|
||||||
return list(friends)
|
return list(friends)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Groups
|
# Groups
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_all_groups(self):
|
def get_all_groups(self):
|
||||||
"""from callbacks"""
|
"""from callbacks"""
|
||||||
@ -71,11 +75,11 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
# failsafe in case there are bogus None groups?
|
# failsafe in case there are bogus None groups?
|
||||||
fgroups = list(filter(lambda x: x, groups))
|
fgroups = list(filter(lambda x: x, groups))
|
||||||
if len(fgroups) != len_groups:
|
if len(fgroups) != len_groups:
|
||||||
LOG_WARN(f"CP.are there are bogus None groups in libtoxcore? {len(fgroups)} != {len_groups}")
|
LOG_WARN(f"are there are bogus None groups in libtoxcore? {len(fgroups)} != {len_groups}")
|
||||||
for group_num in group_numbers:
|
for group_num in group_numbers:
|
||||||
group = self.get_group_by_number(group_num)
|
group = self.get_group_by_number(group_num)
|
||||||
if group is None:
|
if group is None:
|
||||||
LOG_ERROR(f"There are bogus None groups in libtoxcore {group_num}!")
|
LOG_ERROR(f"there are bogus None groups in libtoxcore {group_num}!")
|
||||||
# fixme: do something
|
# fixme: do something
|
||||||
groups = fgroups
|
groups = fgroups
|
||||||
return groups
|
return groups
|
||||||
@ -83,7 +87,7 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
def get_group_by_number(self, group_number):
|
def get_group_by_number(self, group_number):
|
||||||
group = None
|
group = None
|
||||||
try:
|
try:
|
||||||
LOG_INFO(f"CP.CP.group_get_number {group_number} ")
|
LOG_INFO(f"CP.group_get_number {group_number} ")
|
||||||
# original code
|
# original code
|
||||||
chat_id = self._tox.group_get_chat_id(group_number)
|
chat_id = self._tox.group_get_chat_id(group_number)
|
||||||
if chat_id is None:
|
if chat_id is None:
|
||||||
@ -91,16 +95,16 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
elif chat_id == '-1':
|
elif chat_id == '-1':
|
||||||
LOG_ERROR(f"get_group_by_number <0 chat_id ({group_number})")
|
LOG_ERROR(f"get_group_by_number <0 chat_id ({group_number})")
|
||||||
else:
|
else:
|
||||||
LOG_INFO(f"CP.group_get_number {group_number} {chat_id}")
|
LOG_INFO(f"group_get_number {group_number} {chat_id}")
|
||||||
group = self.get_group_by_chat_id(chat_id)
|
group = self.get_group_by_chat_id(chat_id)
|
||||||
if group is None or group == '-1':
|
if group is None or group == '-1':
|
||||||
LOG_WARN(f"CP.get_group_by_number leaving {group} ({group_number})")
|
LOG_WARN(f"get_group_by_number leaving {group} ({group_number})")
|
||||||
#? iRet = self._tox.group_leave(group_number)
|
#? iRet = self._tox.group_leave(group_number)
|
||||||
# invoke in main thread?
|
# invoke in main thread?
|
||||||
# self._contacts_manager.delete_group(group_number)
|
# self._contacts_manager.delete_group(group_number)
|
||||||
return group
|
return group
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG_WARN(f"CP.group_get_number {group_number} {e}")
|
LOG_WARN(f"group_get_number {group_number} {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_group_by_chat_id(self, chat_id):
|
def get_group_by_chat_id(self, chat_id):
|
||||||
@ -127,7 +131,9 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Group peers
|
# Group peers
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_all_group_peers(self):
|
def get_all_group_peers(self):
|
||||||
return list()
|
return list()
|
||||||
@ -142,12 +148,16 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
|
|
||||||
return self._get_group_peer(group, peer)
|
return self._get_group_peer(group, peer)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# All contacts
|
# All contacts
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
return self.get_all_friends() + self.get_all_groups() + self.get_all_group_peers()
|
return self.get_all_friends() + self.get_all_groups() + self.get_all_group_peers()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Caching
|
# Caching
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def clear_cache(self):
|
def clear_cache(self):
|
||||||
self._cache.clear()
|
self._cache.clear()
|
||||||
@ -156,7 +166,9 @@ class ContactProvider(tox_save.ToxSave):
|
|||||||
if contact_public_key in self._cache:
|
if contact_public_key in self._cache:
|
||||||
del self._cache[contact_public_key]
|
del self._cache[contact_public_key]
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _get_contact_from_cache(self, public_key):
|
def _get_contact_from_cache(self, public_key):
|
||||||
return self._cache[public_key] if public_key in self._cache else None
|
return self._cache[public_key] if public_key in self._cache else None
|
||||||
|
@ -105,13 +105,17 @@ class ContactsManager(ToxSave):
|
|||||||
|
|
||||||
return self._contacts[self._active_contact].tox_id == contact.tox_id
|
return self._contacts[self._active_contact].tox_id == contact.tox_id
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Reconnection support
|
# Reconnection support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def reset_contacts_statuses(self):
|
def reset_contacts_statuses(self):
|
||||||
for contact in self._contacts:
|
for contact in self._contacts:
|
||||||
contact.status = None
|
contact.status = None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Work with active friend
|
# Work with active friend
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_active(self):
|
def get_active(self):
|
||||||
return self._active_contact
|
return self._active_contact
|
||||||
@ -200,7 +204,9 @@ class ContactsManager(ToxSave):
|
|||||||
def is_active_a_group_chat_peer(self):
|
def is_active_a_group_chat_peer(self):
|
||||||
return type(self.get_curr_contact()) is GroupPeerContact
|
return type(self.get_curr_contact()) is GroupPeerContact
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Filtration
|
# Filtration
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def filtration_and_sorting(self, sorting=0, filter_str=''):
|
def filtration_and_sorting(self, sorting=0, filter_str=''):
|
||||||
"""
|
"""
|
||||||
@ -280,7 +286,9 @@ class ContactsManager(ToxSave):
|
|||||||
"""
|
"""
|
||||||
self.filtration_and_sorting(self._sorting, self._filter_string)
|
self.filtration_and_sorting(self._sorting, self._filter_string)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Contact getters
|
# Contact getters
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_friend_by_number(self, number):
|
def get_friend_by_number(self, number):
|
||||||
return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0]
|
return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0]
|
||||||
@ -316,7 +324,9 @@ class ContactsManager(ToxSave):
|
|||||||
def is_active_online(self):
|
def is_active_online(self):
|
||||||
return self._active_contact + 1 and self.get_curr_contact().status is not None
|
return self._active_contact + 1 and self.get_curr_contact().status is not None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Work with friends (remove, block, set alias, get public key)
|
# Work with friends (remove, block, set alias, get public key)
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def set_alias(self, num):
|
def set_alias(self, num):
|
||||||
"""
|
"""
|
||||||
@ -401,7 +411,9 @@ class ContactsManager(ToxSave):
|
|||||||
self.add_friend(tox_id)
|
self.add_friend(tox_id)
|
||||||
self.save_profile()
|
self.save_profile()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Groups support
|
# Groups support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_group_chats(self):
|
def get_group_chats(self):
|
||||||
return list(filter(lambda c: type(c) is GroupChat, self._contacts))
|
return list(filter(lambda c: type(c) is GroupChat, self._contacts))
|
||||||
@ -411,7 +423,7 @@ class ContactsManager(ToxSave):
|
|||||||
group = self._contact_provider.get_group_by_number(group_number)
|
group = self._contact_provider.get_group_by_number(group_number)
|
||||||
if group is None:
|
if group is None:
|
||||||
LOG.warn(f"CM.add_group: NULL group from group_number={group_number}")
|
LOG.warn(f"CM.add_group: NULL group from group_number={group_number}")
|
||||||
elif type(group) == int and group < 0:
|
elif group < 0:
|
||||||
LOG.warn(f"CM.add_group: NO group from group={group} group_number={group_number}")
|
LOG.warn(f"CM.add_group: NO group from group={group} group_number={group_number}")
|
||||||
else:
|
else:
|
||||||
LOG.info(f"CM.add_group: Adding group {group._name}")
|
LOG.info(f"CM.add_group: Adding group {group._name}")
|
||||||
@ -429,7 +441,9 @@ class ContactsManager(ToxSave):
|
|||||||
num = self._contacts.index(group)
|
num = self._contacts.index(group)
|
||||||
self._delete_contact(num)
|
self._delete_contact(num)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Groups private messaging
|
# Groups private messaging
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def add_group_peer(self, group, peer):
|
def add_group_peer(self, group, peer):
|
||||||
contact = self._contact_provider.get_group_peer_by_id(group, peer.id)
|
contact = self._contact_provider.get_group_peer_by_id(group, peer.id)
|
||||||
@ -471,7 +485,9 @@ class ContactsManager(ToxSave):
|
|||||||
|
|
||||||
return suggested_names[0]
|
return suggested_names[0]
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Friend requests
|
# Friend requests
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_friend_request(self, sToxPkOrId, message):
|
def send_friend_request(self, sToxPkOrId, message):
|
||||||
"""
|
"""
|
||||||
@ -535,7 +551,9 @@ class ContactsManager(ToxSave):
|
|||||||
def can_send_typing_notification(self):
|
def can_send_typing_notification(self):
|
||||||
return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer()
|
return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Contacts numbers update
|
# Contacts numbers update
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def update_friends_numbers(self):
|
def update_friends_numbers(self):
|
||||||
for friend in self._contact_provider.get_all_friends():
|
for friend in self._contact_provider.get_all_friends():
|
||||||
@ -563,7 +581,9 @@ class ContactsManager(ToxSave):
|
|||||||
for group in groups:
|
for group in groups:
|
||||||
group.remove_all_peers_except_self()
|
group.remove_all_peers_except_self()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _load_contacts(self):
|
def _load_contacts(self):
|
||||||
self._load_friends()
|
self._load_friends()
|
||||||
@ -588,7 +608,9 @@ class ContactsManager(ToxSave):
|
|||||||
def _load_groups(self):
|
def _load_groups(self):
|
||||||
self._contacts.extend(self._contact_provider.get_all_groups())
|
self._contacts.extend(self._contact_provider.get_all_groups())
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Current contact subscriptions
|
# Current contact subscriptions
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _subscribe_to_events(self, contact):
|
def _subscribe_to_events(self, contact):
|
||||||
contact.name_changed_event.add_callback(self._current_contact_name_changed)
|
contact.name_changed_event.add_callback(self._current_contact_name_changed)
|
||||||
|
@ -14,7 +14,9 @@ class Friend(contact.Contact):
|
|||||||
self._receipts = 0
|
self._receipts = 0
|
||||||
self._typing_notification_handler = common.FriendTypingNotificationHandler(number)
|
self._typing_notification_handler = common.FriendTypingNotificationHandler(number)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# File transfers support
|
# File transfers support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def insert_inline(self, before_message_id, inline):
|
def insert_inline(self, before_message_id, inline):
|
||||||
"""
|
"""
|
||||||
@ -50,17 +52,23 @@ class Friend(contact.Contact):
|
|||||||
self._corr = list(filter(lambda m: not (type(m) is UnsentFileMessage and m.message_id == message_id),
|
self._corr = list(filter(lambda m: not (type(m) is UnsentFileMessage and m.message_id == message_id),
|
||||||
self._corr))
|
self._corr))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Full status
|
# Full status
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_full_status(self):
|
def get_full_status(self):
|
||||||
return self._status_message
|
return self._status_message
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Typing notifications
|
# Typing notifications
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_typing_notification_handler(self):
|
def get_typing_notification_handler(self):
|
||||||
return self._typing_notification_handler
|
return self._typing_notification_handler
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Context menu support
|
# Context menu support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_context_menu_generator(self):
|
def get_context_menu_generator(self):
|
||||||
return FriendMenuGenerator(self)
|
return FriendMenuGenerator(self)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from contacts.friend import Friend
|
|
||||||
from common.tox_save import ToxSave
|
from common.tox_save import ToxSave
|
||||||
|
from contacts.friend import Friend
|
||||||
|
|
||||||
|
|
||||||
class FriendFactory(ToxSave):
|
class FriendFactory(ToxSave):
|
||||||
@ -32,7 +32,9 @@ class FriendFactory(ToxSave):
|
|||||||
|
|
||||||
return friend
|
return friend
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _create_friend_item(self):
|
def _create_friend_item(self):
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +35,9 @@ class GroupChat(contact.Contact, ToxSave):
|
|||||||
def get_context_menu_generator(self):
|
def get_context_menu_generator(self):
|
||||||
return GroupMenuGenerator(self)
|
return GroupMenuGenerator(self)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Properties
|
# Properties
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_is_private(self):
|
def get_is_private(self):
|
||||||
return self._is_private
|
return self._is_private
|
||||||
@ -61,7 +63,9 @@ class GroupChat(contact.Contact, ToxSave):
|
|||||||
|
|
||||||
peers_limit = property(get_peers_limit, set_peers_limit)
|
peers_limit = property(get_peers_limit, set_peers_limit)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Peers methods
|
# Peers methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_self_peer(self):
|
def get_self_peer(self):
|
||||||
return self._peers[0]
|
return self._peers[0]
|
||||||
@ -152,7 +156,9 @@ class GroupChat(contact.Contact, ToxSave):
|
|||||||
#
|
#
|
||||||
bans = property(get_bans)
|
bans = property(get_bans)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_default_avatar_path():
|
def _get_default_avatar_path():
|
||||||
|
@ -43,7 +43,9 @@ class GroupFactory(ToxSave):
|
|||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _create_group_item(self):
|
def _create_group_item(self):
|
||||||
"""
|
"""
|
||||||
@ -53,7 +55,7 @@ class GroupFactory(ToxSave):
|
|||||||
return self._items_factory.create_contact_item()
|
return self._items_factory.create_contact_item()
|
||||||
|
|
||||||
def _get_group_number_by_chat_id(self, chat_id):
|
def _get_group_number_by_chat_id(self, chat_id):
|
||||||
for i in range(self._tox.group_get_number_groups()+100):
|
for i in range(self._tox.group_get_number_groups()):
|
||||||
if self._tox.group_get_chat_id(i) == chat_id:
|
if self._tox.group_get_chat_id(i) == chat_id:
|
||||||
return i
|
return i
|
||||||
return -1
|
return -1
|
||||||
|
@ -35,7 +35,9 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave):
|
|||||||
self._waiting_for_reconnection = False
|
self._waiting_for_reconnection = False
|
||||||
self._timer = None
|
self._timer = None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Edit current user's data
|
# Edit current user's data
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def change_status(self):
|
def change_status(self):
|
||||||
"""
|
"""
|
||||||
@ -70,7 +72,9 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave):
|
|||||||
self._sToxId = self._tox.self_get_address()
|
self._sToxId = self._tox.self_get_address()
|
||||||
return self._sToxId
|
return self._sToxId
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Reset
|
# Reset
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from os import chdir, remove, rename
|
from os import chdir, remove, rename
|
||||||
from os.path import basename, getsize, exists, dirname
|
from os.path import basename, dirname, exists, getsize
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from common.event import Event
|
from common.event import Event
|
||||||
from middleware.threads import invoke_in_main_thread
|
from middleware.threads import invoke_in_main_thread
|
||||||
from wrapper.tox import Tox
|
from wrapper.tox import Tox
|
||||||
from wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL
|
from wrapper.toxcore_enums_and_consts import TOX_FILE_CONTROL, TOX_FILE_KIND
|
||||||
|
|
||||||
FILE_TRANSFER_STATE = {
|
FILE_TRANSFER_STATE = {
|
||||||
'RUNNING': 0,
|
'RUNNING': 0,
|
||||||
@ -78,9 +78,6 @@ class FileTransfer:
|
|||||||
|
|
||||||
def get_file_id(self):
|
def get_file_id(self):
|
||||||
return self._file_id
|
return self._file_id
|
||||||
# WTF
|
|
||||||
def get_file_id(self):
|
|
||||||
return self._tox.file_get_file_id(self._friend_number, self._file_number)
|
|
||||||
|
|
||||||
file_id = property(get_file_id)
|
file_id = property(get_file_id)
|
||||||
|
|
||||||
@ -115,6 +112,9 @@ class FileTransfer:
|
|||||||
if self._tox.file_control(self._friend_number, self._file_number, control):
|
if self._tox.file_control(self._friend_number, self._file_number, control):
|
||||||
self.set_state(control)
|
self.set_state(control)
|
||||||
|
|
||||||
|
def get_file_id(self):
|
||||||
|
return self._tox.file_get_file_id(self._friend_number, self._file_number)
|
||||||
|
|
||||||
def _signal(self):
|
def _signal(self):
|
||||||
percentage = self._done / self._size if self._size else 0
|
percentage = self._done / self._size if self._size else 0
|
||||||
if self._creation_time is None or not percentage:
|
if self._creation_time is None or not percentage:
|
||||||
@ -126,7 +126,9 @@ class FileTransfer:
|
|||||||
def _finished(self):
|
def _finished(self):
|
||||||
self._finished_event(self._friend_number, self._file_number)
|
self._finished_event(self._friend_number, self._file_number)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Send file
|
# Send file
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class SendTransfer(FileTransfer):
|
class SendTransfer(FileTransfer):
|
||||||
@ -221,7 +223,9 @@ class SendFromFileBuffer(SendTransfer):
|
|||||||
chdir(dirname(self._path))
|
chdir(dirname(self._path))
|
||||||
remove(self._path)
|
remove(self._path)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Receive file
|
# Receive file
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class ReceiveTransfer(FileTransfer):
|
class ReceiveTransfer(FileTransfer):
|
||||||
|
@ -33,7 +33,9 @@ class FileTransfersHandler(ToxSave):
|
|||||||
self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {}
|
self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {}
|
||||||
self._settings.save()
|
self._settings.save()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# File transfers support
|
# File transfers support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def incoming_file_transfer(self, friend_number, file_number, size, file_name):
|
def incoming_file_transfer(self, friend_number, file_number, size, file_name):
|
||||||
"""
|
"""
|
||||||
@ -253,7 +255,9 @@ class FileTransfersHandler(ToxSave):
|
|||||||
self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, True, ft.total_size()]
|
self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, True, ft.total_size()]
|
||||||
self.cancel_transfer(friend_num, file_num, True)
|
self.cancel_transfer(friend_num, file_num, True)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Avatars support
|
# Avatars support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_avatar(self, friend_number, avatar_path=None):
|
def send_avatar(self, friend_number, avatar_path=None):
|
||||||
"""
|
"""
|
||||||
@ -293,7 +297,9 @@ class FileTransfersHandler(ToxSave):
|
|||||||
for friend in filter(self._is_friend_online, friends):
|
for friend in filter(self._is_friend_online, friends):
|
||||||
self.send_avatar(friend.number)
|
self.send_avatar(friend.number)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _is_friend_online(self, friend_number):
|
def _is_friend_online(self, friend_number):
|
||||||
friend = self._get_friend_by_number(friend_number)
|
friend = self._get_friend_by_number(friend_number)
|
||||||
|
@ -75,7 +75,9 @@ class FileTransfersMessagesService:
|
|||||||
|
|
||||||
return tm
|
return tm
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _is_friend_active(self, friend_number):
|
def _is_friend_active(self, friend_number):
|
||||||
if not self._contacts_manager.is_active_a_friend():
|
if not self._contacts_manager.is_active_a_friend():
|
||||||
|
@ -15,7 +15,9 @@ class GroupChatPeer:
|
|||||||
self._is_muted = is_muted
|
self._is_muted = is_muted
|
||||||
# unused?
|
# unused?
|
||||||
self._kind = 'grouppeer'
|
self._kind = 'grouppeer'
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Readonly properties
|
# Readonly properties
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return self._peer_id
|
return self._peer_id
|
||||||
@ -32,7 +34,9 @@ class GroupChatPeer:
|
|||||||
|
|
||||||
is_current_user = property(get_is_current_user)
|
is_current_user = property(get_is_current_user)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Read-write properties
|
# Read-write properties
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self._name
|
return self._name
|
||||||
|
@ -31,7 +31,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
for group in self._get_all_groups():
|
for group in self._get_all_groups():
|
||||||
group.set_tox(tox)
|
group.set_tox(tox)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Groups creation
|
# Groups creation
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def create_new_gc(self, name, privacy_state, nick, status):
|
def create_new_gc(self, name, privacy_state, nick, status):
|
||||||
try:
|
try:
|
||||||
@ -72,7 +74,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
group.status = constants.TOX_USER_STATUS['NONE']
|
group.status = constants.TOX_USER_STATUS['NONE']
|
||||||
self._contacts_manager.update_filtration()
|
self._contacts_manager.update_filtration()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Groups reconnect and leaving
|
# Groups reconnect and leaving
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def leave_group(self, group_number):
|
def leave_group(self, group_number):
|
||||||
if type(group_number) == int:
|
if type(group_number) == int:
|
||||||
@ -91,7 +95,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
group.status = constants.TOX_USER_STATUS['NONE']
|
group.status = constants.TOX_USER_STATUS['NONE']
|
||||||
self._clear_peers_list(group)
|
self._clear_peers_list(group)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Group invites
|
# Group invites
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def invite_friend(self, friend_number, group_number):
|
def invite_friend(self, friend_number, group_number):
|
||||||
if self._tox.friend_get_connection_status(friend_number) == TOX_CONNECTION['NONE']:
|
if self._tox.friend_get_connection_status(friend_number) == TOX_CONNECTION['NONE']:
|
||||||
@ -136,7 +142,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
|
|
||||||
group_invites_count = property(get_group_invites_count)
|
group_invites_count = property(get_group_invites_count)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Group info methods
|
# Group info methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def update_group_info(self, group):
|
def update_group_info(self, group):
|
||||||
group.name = self._tox.group_get_name(group.number)
|
group.name = self._tox.group_get_name(group.number)
|
||||||
@ -182,7 +190,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
self._tox.group_founder_set_privacy_state(group.number, privacy_state)
|
self._tox.group_founder_set_privacy_state(group.number, privacy_state)
|
||||||
group.is_private = is_private
|
group.is_private = is_private
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Peers list
|
# Peers list
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def generate_peers_list(self):
|
def generate_peers_list(self):
|
||||||
if not self._contacts_manager.is_active_a_group():
|
if not self._contacts_manager.is_active_a_group():
|
||||||
@ -200,7 +210,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
self._screen = widgets_factory.create_self_peer_screen_window(group)
|
self._screen = widgets_factory.create_self_peer_screen_window(group)
|
||||||
self._screen.show()
|
self._screen.show()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Peers actions
|
# Peers actions
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def set_new_peer_role(self, group, peer, role):
|
def set_new_peer_role(self, group, peer, role):
|
||||||
self._tox.group_mod_set_role(group.number, peer.id, role)
|
self._tox.group_mod_set_role(group.number, peer.id, role)
|
||||||
@ -219,7 +231,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
self_peer.status = status
|
self_peer.status = status
|
||||||
self.generate_peers_list()
|
self.generate_peers_list()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Bans support
|
# Bans support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def show_bans_list(self, group):
|
def show_bans_list(self, group):
|
||||||
return
|
return
|
||||||
@ -236,7 +250,9 @@ class GroupsService(tox_save.ToxSave):
|
|||||||
def cancel_ban(self, group_number, ban_id):
|
def cancel_ban(self, group_number, ban_id):
|
||||||
self._tox.group_mod_remove_ban(group_number, ban_id)
|
self._tox.group_mod_remove_ban(group_number, ban_id)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _add_new_group_by_number(self, group_number):
|
def _add_new_group_by_number(self, group_number):
|
||||||
LOG.debug(f"_add_new_group_by_number group_number={group_number}")
|
LOG.debug(f"_add_new_group_by_number group_number={group_number}")
|
||||||
|
@ -3,7 +3,9 @@ from wrapper.toxcore_enums_and_consts import *
|
|||||||
from ui.widgets import *
|
from ui.widgets import *
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Builder
|
# Builder
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class PeerListBuilder:
|
class PeerListBuilder:
|
||||||
@ -61,7 +63,9 @@ class PeerListBuilder:
|
|||||||
self._peers[self._index] = peer
|
self._peers[self._index] = peer
|
||||||
self._index += 1
|
self._index += 1
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Generators
|
# Generators
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class PeersListGenerator:
|
class PeersListGenerator:
|
||||||
|
@ -51,7 +51,9 @@ class Database:
|
|||||||
os.remove(path)
|
os.remove(path)
|
||||||
LOG.info('Db opened: ' +path)
|
LOG.info('Db opened: ' +path)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Public methods
|
# Public methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
if self._toxes.has_password():
|
if self._toxes.has_password():
|
||||||
@ -176,7 +178,9 @@ class Database:
|
|||||||
|
|
||||||
return Database.MessageGetter(self._path, tox_id)
|
return Database.MessageGetter(self._path, tox_id)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Messages loading
|
# Messages loading
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class MessageGetter:
|
class MessageGetter:
|
||||||
|
|
||||||
@ -221,7 +225,9 @@ class Database:
|
|||||||
def _disconnect(self):
|
def _disconnect(self):
|
||||||
self._db.close()
|
self._db.close()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
return connect(self._path, timeout=TIMEOUT)
|
return connect(self._path, timeout=TIMEOUT)
|
||||||
|
@ -22,7 +22,9 @@ class History:
|
|||||||
def set_contacts_manager(self, contacts_manager):
|
def set_contacts_manager(self, contacts_manager):
|
||||||
self._contacts_manager = contacts_manager
|
self._contacts_manager = contacts_manager
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# History support
|
# History support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def save_history(self):
|
def save_history(self):
|
||||||
"""
|
"""
|
||||||
@ -126,7 +128,9 @@ class History:
|
|||||||
|
|
||||||
return generator.generate()
|
return generator.generate()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Items creation
|
# Items creation
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _create_message_item(self, message):
|
def _create_message_item(self, message):
|
||||||
return self._messages_items_factory.create_message_item(message, False)
|
return self._messages_items_factory.create_message_item(message, False)
|
||||||
|
@ -192,8 +192,8 @@ def main_parser(_=None, iMode=2):
|
|||||||
parser.add_argument('--auto_accept_path', '--auto-accept-path', type=str,
|
parser.add_argument('--auto_accept_path', '--auto-accept-path', type=str,
|
||||||
default=os.path.join(os.environ['HOME'], 'Downloads'),
|
default=os.path.join(os.environ['HOME'], 'Downloads'),
|
||||||
help="auto_accept_path")
|
help="auto_accept_path")
|
||||||
# parser.add_argument('--mode', type=int, default=iMode,
|
parser.add_argument('--mode', type=int, default=iMode,
|
||||||
# help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0')
|
help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0')
|
||||||
parser.add_argument('--font', type=str, default="Courier",
|
parser.add_argument('--font', type=str, default="Courier",
|
||||||
help='Message font')
|
help='Message font')
|
||||||
parser.add_argument('--message_font_size', type=int, default=15,
|
parser.add_argument('--message_font_size', type=int, default=15,
|
||||||
|
@ -38,7 +38,9 @@ class Messenger(tox_save.ToxSave):
|
|||||||
|
|
||||||
return contact.get_last_message_text()
|
return contact.get_last_message_text()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Messaging - friends
|
# Messaging - friends
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def new_message(self, friend_number, message_type, message):
|
def new_message(self, friend_number, message_type, message):
|
||||||
"""
|
"""
|
||||||
@ -138,7 +140,9 @@ class Messenger(tox_save.ToxSave):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.warn('Sending pending messages failed with ' + str(ex))
|
LOG.warn('Sending pending messages failed with ' + str(ex))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Messaging - groups
|
# Messaging - groups
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_message_to_group(self, text, message_type, group_number=None):
|
def send_message_to_group(self, text, message_type, group_number=None):
|
||||||
if group_number is None:
|
if group_number is None:
|
||||||
@ -179,7 +183,9 @@ class Messenger(tox_save.ToxSave):
|
|||||||
text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), t, message_type)
|
text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), t, message_type)
|
||||||
self._add_message(text_message, group)
|
self._add_message(text_message, group)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Messaging - group peers
|
# Messaging - group peers
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_message_to_group_peer(self, text, message_type, group_number=None, peer_id=None):
|
def send_message_to_group_peer(self, text, message_type, group_number=None, peer_id=None):
|
||||||
if group_number is None or peer_id is None:
|
if group_number is None or peer_id is None:
|
||||||
@ -232,13 +238,17 @@ class Messenger(tox_save.ToxSave):
|
|||||||
return
|
return
|
||||||
self._add_message(text_message, group_peer_contact)
|
self._add_message(text_message, group_peer_contact)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Message receipts
|
# Message receipts
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def receipt(self, friend_number, message_id):
|
def receipt(self, friend_number, message_id):
|
||||||
friend = self._get_friend_by_number(friend_number)
|
friend = self._get_friend_by_number(friend_number)
|
||||||
friend.mark_as_sent(message_id)
|
friend.mark_as_sent(message_id)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Typing notifications
|
# Typing notifications
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_typing(self, typing):
|
def send_typing(self, typing):
|
||||||
"""
|
"""
|
||||||
@ -256,7 +266,9 @@ class Messenger(tox_save.ToxSave):
|
|||||||
if self._contacts_manager.is_friend_active(friend_number):
|
if self._contacts_manager.is_friend_active(friend_number):
|
||||||
self._screen.typing.setVisible(typing)
|
self._screen.typing.setVisible(typing)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Contact info updated
|
# Contact info updated
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def new_friend_name(self, friend, old_name, new_name):
|
def new_friend_name(self, friend, old_name, new_name):
|
||||||
if old_name == new_name or friend.has_alias():
|
if old_name == new_name or friend.has_alias():
|
||||||
@ -267,7 +279,9 @@ class Messenger(tox_save.ToxSave):
|
|||||||
friend.actions = True
|
friend.actions = True
|
||||||
self._add_info_message(friend.number, message)
|
self._add_info_message(friend.number, message)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _split_message(message):
|
def _split_message(message):
|
||||||
|
@ -46,7 +46,9 @@ def bTooSoon(key, sSlot, fSec=10.0):
|
|||||||
|
|
||||||
# TODO: refactoring. Use contact provider instead of manager
|
# TODO: refactoring. Use contact provider instead of manager
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - current user
|
# Callbacks - current user
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
global iBYTES
|
global iBYTES
|
||||||
iBYTES=0
|
iBYTES=0
|
||||||
@ -93,7 +95,9 @@ def self_connection_status(tox, profile):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - friends
|
# Callbacks - friends
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def friend_status(contacts_manager, file_transfer_handler, profile, settings):
|
def friend_status(contacts_manager, file_transfer_handler, profile, settings):
|
||||||
@ -231,7 +235,9 @@ def friend_read_receipt(messenger):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - file transfers
|
# Callbacks - file transfers
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings):
|
def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings):
|
||||||
@ -306,7 +312,9 @@ def file_recv_control(file_transfer_handler):
|
|||||||
|
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - custom packets
|
# Callbacks - custom packets
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def lossless_packet(plugin_loader):
|
def lossless_packet(plugin_loader):
|
||||||
@ -331,7 +339,9 @@ def lossy_packet(plugin_loader):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - audio
|
# Callbacks - audio
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def call_state(calls_manager):
|
def call_state(calls_manager):
|
||||||
def wrapped(iToxav, friend_number, mask, user_data):
|
def wrapped(iToxav, friend_number, mask, user_data):
|
||||||
@ -374,7 +384,9 @@ def callback_audio(calls_manager):
|
|||||||
|
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - video
|
# Callbacks - video
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data):
|
def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data):
|
||||||
@ -432,7 +444,9 @@ def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, u
|
|||||||
LOG_ERROR(f"video_receive_frame {ex} #{friend_number}")
|
LOG_ERROR(f"video_receive_frame {ex} #{friend_number}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - groups
|
# Callbacks - groups
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def group_message(window, tray, tox, messenger, settings, profile):
|
def group_message(window, tray, tox, messenger, settings, profile):
|
||||||
@ -700,7 +714,9 @@ def group_privacy_state(contacts_provider):
|
|||||||
|
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - initialization
|
# Callbacks - initialization
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager,
|
def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager,
|
||||||
|
@ -28,7 +28,9 @@ def LOG_TRACE(l): pass # print('TRACE+ '+l)
|
|||||||
iLAST_CONN = 0
|
iLAST_CONN = 0
|
||||||
iLAST_DELTA = 60
|
iLAST_DELTA = 60
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Base threads
|
# Base threads
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class BaseThread(threading.Thread):
|
class BaseThread(threading.Thread):
|
||||||
|
|
||||||
@ -72,7 +74,9 @@ class BaseQThread(QtCore.QThread):
|
|||||||
else:
|
else:
|
||||||
LOG_WARN(f"BaseQThread {self.name} BLOCKED")
|
LOG_WARN(f"BaseQThread {self.name} BLOCKED")
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Toxcore threads
|
# Toxcore threads
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class InitThread(BaseThread):
|
class InitThread(BaseThread):
|
||||||
|
|
||||||
@ -159,7 +163,9 @@ class ToxAVIterateThread(BaseQThread):
|
|||||||
sleep(self._toxav.iteration_interval() / 1000)
|
sleep(self._toxav.iteration_interval() / 1000)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# File transfers thread
|
# File transfers thread
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class FileTransfersThread(BaseQThread):
|
class FileTransfersThread(BaseQThread):
|
||||||
|
|
||||||
@ -197,7 +203,9 @@ def execute(func, *args, **kwargs):
|
|||||||
_thread.execute(func, *args, **kwargs)
|
_thread.execute(func, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Invoking in main thread
|
# Invoking in main thread
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
class InvokeEvent(QtCore.QEvent):
|
||||||
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
# Plugins
|
|
||||||
|
|
||||||
Repo with plugins for [Toxygen](https://macaw.me/emdee/toxygen/)
|
|
||||||
|
|
||||||
For more info visit [plugins.md](https://macaw.me/emdee/toxygen/blob/master/docs/plugins.md) and [plugin_api.md](https://github.com/toxygen-project[/toxygen/blob/master/docs/plugin-api.md)
|
|
||||||
|
|
||||||
# Plugins list:
|
|
||||||
|
|
||||||
- ToxId - share your Tox ID and copy friend's Tox ID easily.
|
|
||||||
- MarqueeStatus - create ticker from your status message.
|
|
||||||
- BirthDay - get notifications on your friends' birthdays.
|
|
||||||
- Bot - bot which can communicate with your friends when you are away.
|
|
||||||
- SearchPlugin - select text in message and find it in search engine.
|
|
||||||
- AutoAwayStatusLinux - sets "Away" status when user is inactive (Linux only).
|
|
||||||
- AutoAwayStatusWindows - sets "Away" status when user is inactive (Windows only).
|
|
||||||
- Chess - play chess with your friends using Tox.
|
|
||||||
- Garland - changes your status like it's garland.
|
|
||||||
- AutoAnswer - calls auto answering.
|
|
||||||
- uToxInlineSending - send inlines with the same name as uTox does.
|
|
||||||
- AvatarEncryption - encrypt all avatars using profile password
|
|
||||||
|
|
||||||
## Hard fork
|
|
||||||
|
|
||||||
Not all of these are working...
|
|
||||||
|
|
||||||
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!
|
|
@ -1,83 +0,0 @@
|
|||||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
||||||
import plugin_super_class
|
|
||||||
import json
|
|
||||||
from user_data import settings
|
|
||||||
import os
|
|
||||||
from bootstrap.bootstrap import get_user_config_path
|
|
||||||
|
|
||||||
class AvatarEncryption(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(AvatarEncryption, self).__init__('AvatarEncryption', 'ae', *args)
|
|
||||||
self._path = os.path.join(get_user_config_path(), 'avatars')
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile = self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
#was self._contacts = self._profile._contacts[:]
|
|
||||||
self._contacts = self._profile._contacts_provider.get_all_friends()
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("AvatarEncryption", 'Encrypt all avatars using profile password.')
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if not self._encrypt_save.has_password():
|
|
||||||
return
|
|
||||||
i, data = 1, {}
|
|
||||||
|
|
||||||
self.save_contact_avatar(data, self._profile, 0)
|
|
||||||
for friend in self._contacts:
|
|
||||||
self.save_contact_avatar(data, friend, i)
|
|
||||||
i += 1
|
|
||||||
self.save_settings(json.dumps(data))
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
if not self._encrypt_save.has_password():
|
|
||||||
return
|
|
||||||
data = json.loads(self.load_settings())
|
|
||||||
|
|
||||||
self.load_contact_avatar(data, self._profile)
|
|
||||||
for friend in self._contacts:
|
|
||||||
self.load_contact_avatar(data, friend)
|
|
||||||
self._profile.update()
|
|
||||||
|
|
||||||
def save_contact_avatar(self, data, contact, i):
|
|
||||||
tox_id = contact.tox_id[:64]
|
|
||||||
data[str(tox_id)] = str(i)
|
|
||||||
path = os.path.join(self._path, tox_id + '.png')
|
|
||||||
if os.path.isfile(path):
|
|
||||||
with open(path, 'rb') as fl:
|
|
||||||
avatar = fl.read()
|
|
||||||
encr_avatar = self._encrypt_save.pass_encrypt(avatar)
|
|
||||||
with open(os.path.join(self._path, self._settings.name + '_' + str(i) + '.png'), 'wb') as fl:
|
|
||||||
fl.write(encr_avatar)
|
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
def load_contact_avatar(self, data, contact):
|
|
||||||
tox_id = str(contact.tox_id[:64])
|
|
||||||
if tox_id not in data:
|
|
||||||
return
|
|
||||||
path = os.path.join(self._path, self._settings.name + '_' + data[tox_id] + '.png')
|
|
||||||
if os.path.isfile(path):
|
|
||||||
with open(path, 'rb') as fl:
|
|
||||||
avatar = fl.read()
|
|
||||||
decr_avatar = self._encrypt_save.pass_decrypt(avatar)
|
|
||||||
with open(os.path.join(self._path, str(tox_id) + '.png'), 'wb') as fl:
|
|
||||||
fl.write(decr_avatar)
|
|
||||||
os.remove(path)
|
|
||||||
contact.load_avatar()
|
|
||||||
|
|
||||||
def load_settings(self):
|
|
||||||
try:
|
|
||||||
with open(plugin_super_class.path_to_data(self._short_name) + self._settings.name + '.json', 'rb') as fl:
|
|
||||||
data = fl.read()
|
|
||||||
return str(self._encrypt_save.pass_decrypt(data), 'utf-8') if data else '{}'
|
|
||||||
except:
|
|
||||||
return '{}'
|
|
||||||
|
|
||||||
def save_settings(self, data):
|
|
||||||
try:
|
|
||||||
data = self._encrypt_save.pass_encrypt(bytes(data, 'utf-8'))
|
|
||||||
with open(plugin_super_class.path_to_data(self._short_name) + self._settings.name + '.json', 'wb') as fl:
|
|
||||||
fl.write(data)
|
|
||||||
except:
|
|
||||||
pass
|
|
@ -1,111 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
|
||||||
from subprocess import check_output
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
|
||||||
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
|
||||||
|
|
||||||
def __init__(self, fn, *args, **kwargs):
|
|
||||||
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
|
|
||||||
self.fn = fn
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
|
|
||||||
class Invoker(QtCore.QObject):
|
|
||||||
|
|
||||||
def event(self, event):
|
|
||||||
event.fn(*event.args, **event.kwargs)
|
|
||||||
return True
|
|
||||||
|
|
||||||
_invoker = Invoker()
|
|
||||||
|
|
||||||
|
|
||||||
def invoke_in_main_thread(fn, *args, **kwargs):
|
|
||||||
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class AutoAwayStatusLinux(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super().__init__('AutoAwayStatusLinux', 'awayl', *args)
|
|
||||||
self._thread = None
|
|
||||||
self._exec = None
|
|
||||||
self._active = False
|
|
||||||
self._time = json.loads(self.load_settings())['time']
|
|
||||||
self._prev_status = 0
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile=self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("AutoAwayStatusLinux", 'sets "Away" status when user is inactive (Linux only).')
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._exec = False
|
|
||||||
if self._active:
|
|
||||||
self._thread.join()
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._exec = True
|
|
||||||
self._thread = threading.Thread(target=self.loop)
|
|
||||||
self._thread.start()
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.save_settings('{"time": ' + str(self._time) + '}')
|
|
||||||
|
|
||||||
def change_status(self, status=1):
|
|
||||||
if self._profile.status in (0, 2):
|
|
||||||
self._prev_status = self._profile.status
|
|
||||||
if status is not None:
|
|
||||||
invoke_in_main_thread(self._profile.set_status, status)
|
|
||||||
|
|
||||||
def get_window(self):
|
|
||||||
inst = self
|
|
||||||
|
|
||||||
class Window(QtWidgets.QWidget):
|
|
||||||
def __init__(self):
|
|
||||||
super(Window, self).__init__()
|
|
||||||
self.setGeometry(QtCore.QRect(450, 300, 350, 100))
|
|
||||||
self.label = QtWidgets.QLabel(self)
|
|
||||||
self.label.setGeometry(QtCore.QRect(20, 0, 310, 35))
|
|
||||||
self.label.setText(QtWidgets.QApplication.translate("AutoAwayStatusLinux", "Auto away time in minutes\n(0 - to disable)"))
|
|
||||||
self.time = QtWidgets.QLineEdit(self)
|
|
||||||
self.time.setGeometry(QtCore.QRect(20, 40, 310, 25))
|
|
||||||
self.time.setText(str(inst._time))
|
|
||||||
self.setWindowTitle("AutoAwayStatusLinux")
|
|
||||||
self.ok = QtWidgets.QPushButton(self)
|
|
||||||
self.ok.setGeometry(QtCore.QRect(20, 70, 310, 25))
|
|
||||||
self.ok.setText(
|
|
||||||
QtWidgets.QApplication.translate("AutoAwayStatusLinux", "Save"))
|
|
||||||
self.ok.clicked.connect(self.update)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
try:
|
|
||||||
t = int(self.time.text())
|
|
||||||
except:
|
|
||||||
t = 0
|
|
||||||
inst._time = t
|
|
||||||
inst.save()
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
return Window()
|
|
||||||
|
|
||||||
def loop(self):
|
|
||||||
self._active = True
|
|
||||||
while self._exec:
|
|
||||||
time.sleep(5)
|
|
||||||
d = check_output(['xprintidle'])
|
|
||||||
d = int(d) // 1000
|
|
||||||
if self._time:
|
|
||||||
if d > 60 * self._time:
|
|
||||||
self.change_status()
|
|
||||||
elif self._profile.status == 1:
|
|
||||||
self.change_status(self._prev_status)
|
|
@ -1,115 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
|
||||||
from ctypes import Structure, windll, c_uint, sizeof, byref
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class LASTINPUTINFO(Structure):
|
|
||||||
_fields_ = [('cbSize', c_uint), ('dwTime', c_uint)]
|
|
||||||
|
|
||||||
|
|
||||||
def get_idle_duration():
|
|
||||||
lastInputInfo = LASTINPUTINFO()
|
|
||||||
lastInputInfo.cbSize = sizeof(lastInputInfo)
|
|
||||||
windll.user32.GetLastInputInfo(byref(lastInputInfo))
|
|
||||||
millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime
|
|
||||||
return millis / 1000.0
|
|
||||||
|
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
|
||||||
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
|
||||||
|
|
||||||
def __init__(self, fn, *args, **kwargs):
|
|
||||||
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
|
|
||||||
self.fn = fn
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
|
|
||||||
class Invoker(QtCore.QObject):
|
|
||||||
|
|
||||||
def event(self, event):
|
|
||||||
event.fn(*event.args, **event.kwargs)
|
|
||||||
return True
|
|
||||||
|
|
||||||
_invoker = Invoker()
|
|
||||||
|
|
||||||
|
|
||||||
def invoke_in_main_thread(fn, *args, **kwargs):
|
|
||||||
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class AutoAwayStatusWindows(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super().__init__('AutoAwayStatusWindows', 'awayw', *args)
|
|
||||||
self._thread = None
|
|
||||||
self._exec = None
|
|
||||||
self._active = False
|
|
||||||
self._time = json.loads(self.load_settings())['time']
|
|
||||||
self._prev_status = 0
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._exec = False
|
|
||||||
if self._active:
|
|
||||||
self._thread.join()
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._exec = True
|
|
||||||
self._thread = threading.Thread(target=self.loop)
|
|
||||||
self._thread.start()
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.save_settings('{"time": ' + str(self._time) + '}')
|
|
||||||
|
|
||||||
def change_status(self, status=1):
|
|
||||||
if self._profile.status != 1:
|
|
||||||
self._prev_status = self._profile.status
|
|
||||||
invoke_in_main_thread(self._profile.set_status, status)
|
|
||||||
|
|
||||||
def get_window(self):
|
|
||||||
inst = self
|
|
||||||
|
|
||||||
class Window(QtWidgets.QWidget):
|
|
||||||
def __init__(self):
|
|
||||||
super(Window, self).__init__()
|
|
||||||
self.setGeometry(QtCore.QRect(450, 300, 350, 100))
|
|
||||||
self.label = QtWidgets.QLabel(self)
|
|
||||||
self.label.setGeometry(QtCore.QRect(20, 0, 310, 35))
|
|
||||||
self.label.setText(QtWidgets.QApplication.translate("AutoAwayStatusWindows", "Auto away time in minutes\n(0 - to disable)"))
|
|
||||||
self.time = QtWidgets.QLineEdit(self)
|
|
||||||
self.time.setGeometry(QtCore.QRect(20, 40, 310, 25))
|
|
||||||
self.time.setText(str(inst._time))
|
|
||||||
self.setWindowTitle("AutoAwayStatusWindows")
|
|
||||||
self.ok = QtWidgets.QPushButton(self)
|
|
||||||
self.ok.setGeometry(QtCore.QRect(20, 70, 310, 25))
|
|
||||||
self.ok.setText(
|
|
||||||
QtWidgets.QApplication.translate("AutoAwayStatusWindows", "Save"))
|
|
||||||
self.ok.clicked.connect(self.update)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
try:
|
|
||||||
t = int(self.time.text())
|
|
||||||
except:
|
|
||||||
t = 0
|
|
||||||
inst._time = t
|
|
||||||
inst.save()
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
return Window()
|
|
||||||
|
|
||||||
def loop(self):
|
|
||||||
self._active = True
|
|
||||||
while self._exec:
|
|
||||||
time.sleep(5)
|
|
||||||
d = get_idle_duration()
|
|
||||||
if self._time:
|
|
||||||
if d > 60 * self._time:
|
|
||||||
self.change_status()
|
|
||||||
elif self._profile.status == 1:
|
|
||||||
self.change_status(self._prev_status)
|
|
@ -1,2 +0,0 @@
|
|||||||
SOURCES = bday.py
|
|
||||||
TRANSLATIONS = bday/en_GB.ts bday/en_US.ts bday/ru_RU.ts
|
|
@ -1,95 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
from PyQt5 import QtWidgets, QtCore
|
|
||||||
import json
|
|
||||||
import importlib
|
|
||||||
|
|
||||||
|
|
||||||
class BirthDay(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
# Constructor. In plugin __init__ should take only 1 last argument
|
|
||||||
super(BirthDay, self).__init__('BirthDay', 'bday', *args)
|
|
||||||
self._data = json.loads(self.load_settings())
|
|
||||||
self._datetime = importlib.import_module('datetime')
|
|
||||||
self._timers = []
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile=self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
now = self._datetime.datetime.now()
|
|
||||||
today = {}
|
|
||||||
x = self._profile.tox_id[:64]
|
|
||||||
for key in self._data:
|
|
||||||
if key != x and key != 'send_date':
|
|
||||||
arr = self._data[key].split('.')
|
|
||||||
if int(arr[0]) == now.day and int(arr[1]) == now.month:
|
|
||||||
today[key] = now.year - int(arr[2])
|
|
||||||
if len(today):
|
|
||||||
msgbox = QtWidgets.QMessageBox()
|
|
||||||
title = QtWidgets.QApplication.translate('BirthDay', "Birthday!")
|
|
||||||
msgbox.setWindowTitle(title)
|
|
||||||
text = ', '.join(self._profile.get_friend_by_number(self._tox.friend_by_public_key(x)).name + ' ({})'.format(today[x]) for x in today)
|
|
||||||
msgbox.setText('Birthdays: ' + text)
|
|
||||||
msgbox.exec_()
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("BirthDay", "Send and get notifications on your friends' birthdays.")
|
|
||||||
|
|
||||||
def get_window(self):
|
|
||||||
inst = self
|
|
||||||
x = self._profile.tox_id[:64]
|
|
||||||
|
|
||||||
class Window(QtWidgets.QWidget):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Window, self).__init__()
|
|
||||||
self.setGeometry(QtCore.QRect(450, 300, 350, 150))
|
|
||||||
self.send = QtWidgets.QCheckBox(self)
|
|
||||||
self.send.setGeometry(QtCore.QRect(20, 10, 310, 25))
|
|
||||||
self.send.setText(QtWidgets.QApplication.translate('BirthDay', "Send my birthday date to contacts"))
|
|
||||||
self.setWindowTitle(QtWidgets.QApplication.translate('BirthDay', "Birthday"))
|
|
||||||
self.send.clicked.connect(self.update)
|
|
||||||
self.send.setChecked(inst._data['send_date'])
|
|
||||||
self.date = QtWidgets.QLineEdit(self)
|
|
||||||
self.date.setGeometry(QtCore.QRect(20, 50, 310, 25))
|
|
||||||
self.date.setPlaceholderText(QtWidgets.QApplication.translate('BirthDay', "Date in format dd.mm.yyyy"))
|
|
||||||
self.set_date = QtWidgets.QPushButton(self)
|
|
||||||
self.set_date.setGeometry(QtCore.QRect(20, 90, 310, 25))
|
|
||||||
self.set_date.setText(QtWidgets.QApplication.translate('BirthDay', "Save date"))
|
|
||||||
self.set_date.clicked.connect(self.save_curr_date)
|
|
||||||
self.date.setText(inst._data[x] if x in inst._data else '')
|
|
||||||
|
|
||||||
def save_curr_date(self):
|
|
||||||
inst._data[x] = self.date.text()
|
|
||||||
inst.save_settings(json.dumps(inst._data))
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
inst._data['send_date'] = self.send.isChecked()
|
|
||||||
inst.save_settings(json.dumps(inst._data))
|
|
||||||
|
|
||||||
if not hasattr(self, '_window') or not self._window:
|
|
||||||
self._window = Window()
|
|
||||||
return self._window
|
|
||||||
|
|
||||||
def lossless_packet(self, data, friend_number):
|
|
||||||
if len(data):
|
|
||||||
friend = self._profile.get_friend_by_number(friend_number)
|
|
||||||
self._data[friend.tox_id] = data
|
|
||||||
self.save_settings(json.dumps(self._data))
|
|
||||||
elif self._data['send_date'] and self._profile.tox_id[:64] in self._data:
|
|
||||||
self.send_lossless(self._data[self._profile.tox_id[:64]], friend_number)
|
|
||||||
|
|
||||||
def friend_connected(self, friend_number):
|
|
||||||
timer = QtCore.QTimer()
|
|
||||||
timer.timeout.connect(lambda: self.timer(friend_number))
|
|
||||||
timer.start(10000)
|
|
||||||
self._timers.append(timer)
|
|
||||||
|
|
||||||
def timer(self, friend_number):
|
|
||||||
timer = self._timers.pop()
|
|
||||||
timer.stop()
|
|
||||||
if self._profile.get_friend_by_number(friend_number).tox_id not in self._data:
|
|
||||||
self.send_lossless('', friend_number)
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
|
||||||
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
|
||||||
|
|
||||||
def __init__(self, fn, *args, **kwargs):
|
|
||||||
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
|
|
||||||
self.fn = fn
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
|
|
||||||
class Invoker(QtCore.QObject):
|
|
||||||
|
|
||||||
def event(self, event):
|
|
||||||
event.fn(*event.args, **event.kwargs)
|
|
||||||
return True
|
|
||||||
|
|
||||||
_invoker = Invoker()
|
|
||||||
|
|
||||||
|
|
||||||
def invoke_in_main_thread(fn, *args, **kwargs):
|
|
||||||
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class Bot(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(Bot, self).__init__('Bot', 'bot', *args)
|
|
||||||
self._callback = None
|
|
||||||
self._mode = 0
|
|
||||||
self._message = "I'm away, will back soon"
|
|
||||||
self._timer = QtCore.QTimer()
|
|
||||||
self._timer.timeout.connect(self.initialize)
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile=self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("Bot", 'Plugin to answer bot to your friends.')
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._timer.start(10000)
|
|
||||||
|
|
||||||
def command(self, command):
|
|
||||||
if command.startswith('mode '):
|
|
||||||
self._mode = int(command.split(' ')[-1])
|
|
||||||
elif command.startswith('message '):
|
|
||||||
self._message = command[8:]
|
|
||||||
else:
|
|
||||||
super().command(command)
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
self._timer.stop()
|
|
||||||
self._callback = self._tox.friend_message_cb
|
|
||||||
|
|
||||||
def incoming_message(tox, friend_number, message_type, message, size, user_data):
|
|
||||||
self._callback(tox, friend_number, message_type, message, size, user_data)
|
|
||||||
if self._profile.status == 1: # TOX_USER_STATUS['AWAY']
|
|
||||||
self.answer(friend_number, str(message, 'utf-8'))
|
|
||||||
|
|
||||||
self._tox.callback_friend_message(incoming_message) # , None
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
if not self._callback: return
|
|
||||||
try:
|
|
||||||
# TypeError: argument must be callable or integer function address
|
|
||||||
self._tox.callback_friend_message(self._callback) # , None
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def answer(self, friend_number, message):
|
|
||||||
if not self._mode:
|
|
||||||
message = self._message
|
|
||||||
invoke_in_main_thread(self._profile.send_message, message, friend_number)
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE TS><TS version="1.1">
|
|
||||||
<context>
|
|
||||||
<name>BirthDay</name>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="28"/>
|
|
||||||
<source>Birthday!</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="44"/>
|
|
||||||
<source>Send my birthday date to contacts</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="45"/>
|
|
||||||
<source>Birthday</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="50"/>
|
|
||||||
<source>Date in format dd.mm.yyyy</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="53"/>
|
|
||||||
<source>Save date</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
</TS>
|
|
@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE TS><TS version="1.1">
|
|
||||||
<context>
|
|
||||||
<name>BirthDay</name>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="28"/>
|
|
||||||
<source>Birthday!</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="44"/>
|
|
||||||
<source>Send my birthday date to contacts</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="45"/>
|
|
||||||
<source>Birthday</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="50"/>
|
|
||||||
<source>Date in format dd.mm.yyyy</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="53"/>
|
|
||||||
<source>Save date</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
</TS>
|
|
@ -1,75 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
|
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
|
||||||
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
|
||||||
|
|
||||||
def __init__(self, fn, *args, **kwargs):
|
|
||||||
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
|
|
||||||
self.fn = fn
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
|
|
||||||
class Invoker(QtCore.QObject):
|
|
||||||
|
|
||||||
def event(self, event):
|
|
||||||
event.fn(*event.args, **event.kwargs)
|
|
||||||
return True
|
|
||||||
|
|
||||||
_invoker = Invoker()
|
|
||||||
|
|
||||||
|
|
||||||
def invoke_in_main_thread(fn, *args, **kwargs):
|
|
||||||
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class Garland(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(Garland, self).__init__('Garland', 'grlnd', *args)
|
|
||||||
self._thread = None
|
|
||||||
self._exec = None
|
|
||||||
self._time = 3
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile=self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("Garland", "Changes your status like it's garland.")
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._exec = False
|
|
||||||
self._thread.join()
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._exec = True
|
|
||||||
self._thread = threading.Thread(target=self.change_status)
|
|
||||||
self._thread.start()
|
|
||||||
|
|
||||||
def command(self, command):
|
|
||||||
if command.startswith('time'):
|
|
||||||
self._time = max(int(command.split(' ')[1]), 300) / 1000
|
|
||||||
else:
|
|
||||||
super().command(command)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
if hasattr(self, '_profile'):
|
|
||||||
if not hasattr(self._profile, 'status') or not self._profile.status:
|
|
||||||
retval = 0
|
|
||||||
else:
|
|
||||||
retval = (self._profile.status + 1) % 3
|
|
||||||
self._profile.set_status(retval)
|
|
||||||
|
|
||||||
def change_status(self):
|
|
||||||
time.sleep(5)
|
|
||||||
while self._exec:
|
|
||||||
invoke_in_main_thread(self.update)
|
|
||||||
time.sleep(self._time)
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
|||||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
||||||
import plugin_super_class
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
|
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
|
||||||
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
|
|
||||||
|
|
||||||
def __init__(self, fn, *args, **kwargs):
|
|
||||||
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
|
|
||||||
self.fn = fn
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
|
|
||||||
class Invoker(QtCore.QObject):
|
|
||||||
|
|
||||||
def event(self, event):
|
|
||||||
event.fn(*event.args, **event.kwargs)
|
|
||||||
return True
|
|
||||||
|
|
||||||
_invoker = Invoker()
|
|
||||||
|
|
||||||
def invoke_in_main_thread(fn, *args, **kwargs):
|
|
||||||
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class MarqueeStatus(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(MarqueeStatus, self).__init__('MarqueeStatus', 'mrq', *args)
|
|
||||||
self._thread = None
|
|
||||||
self._exec = None
|
|
||||||
self.active = False
|
|
||||||
self.left = True
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile=self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("MarqueeStatus", 'Create ticker from your status message.')
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._exec = False
|
|
||||||
if self.active:
|
|
||||||
self._thread.join()
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._exec = True
|
|
||||||
self._thread = threading.Thread(target=self.change_status)
|
|
||||||
self._thread.start()
|
|
||||||
|
|
||||||
def command(self, command):
|
|
||||||
if command == 'rev':
|
|
||||||
self.left = not self.left
|
|
||||||
else:
|
|
||||||
super(MarqueeStatus, self).command(command)
|
|
||||||
|
|
||||||
def set_status_message(self):
|
|
||||||
message = str(self._profile.status_message)
|
|
||||||
if self.left:
|
|
||||||
self._profile.set_status_message(bytes(message[1:] + message[0], 'utf-8'))
|
|
||||||
else:
|
|
||||||
self._profile.set_status_message(bytes(message[-1] + message[:-1], 'utf-8'))
|
|
||||||
|
|
||||||
def init_status(self):
|
|
||||||
self._profile.status_message = bytes(self._profile.status_message.strip() + ' ', 'utf-8')
|
|
||||||
|
|
||||||
def change_status(self):
|
|
||||||
self.active = True
|
|
||||||
if hasattr(self, '_profile'):
|
|
||||||
tmp = self._profile.status_message
|
|
||||||
time.sleep(10)
|
|
||||||
invoke_in_main_thread(self.init_status)
|
|
||||||
while self._exec:
|
|
||||||
time.sleep(1)
|
|
||||||
if self._profile.status is not None:
|
|
||||||
invoke_in_main_thread(self.set_status_message)
|
|
||||||
invoke_in_main_thread(self._profile.set_status_message, bytes(tmp, 'utf-8'))
|
|
||||||
self.active = False
|
|
||||||
|
|
@ -53,7 +53,9 @@ class PluginSuperClass(tox_save.ToxSave):
|
|||||||
self._short_name = short_name[:MAX_SHORT_NAME_LENGTH]
|
self._short_name = short_name[:MAX_SHORT_NAME_LENGTH]
|
||||||
self._translator = None # translator for plugin's GUI
|
self._translator = None # translator for plugin's GUI
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Get methods
|
# Get methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""
|
"""
|
||||||
@ -96,7 +98,9 @@ class PluginSuperClass(tox_save.ToxSave):
|
|||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Plugin was stopped, started or new command received
|
# Plugin was stopped, started or new command received
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
@ -126,7 +130,9 @@ class PluginSuperClass(tox_save.ToxSave):
|
|||||||
title = util_ui.tr('List of commands for plugin {}').format(self._name)
|
title = util_ui.tr('List of commands for plugin {}').format(self._name)
|
||||||
util_ui.message_box(text, title)
|
util_ui.message_box(text, title)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Translations support
|
# Translations support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def load_translator(self):
|
def load_translator(self):
|
||||||
"""
|
"""
|
||||||
@ -143,7 +149,9 @@ class PluginSuperClass(tox_save.ToxSave):
|
|||||||
self._translator.load(path_to_data(self._short_name) + lang_path)
|
self._translator.load(path_to_data(self._short_name) + lang_path)
|
||||||
app.installTranslator(self._translator)
|
app.installTranslator(self._translator)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Settings loading and saving
|
# Settings loading and saving
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def load_settings(self):
|
def load_settings(self):
|
||||||
"""
|
"""
|
||||||
@ -162,7 +170,9 @@ class PluginSuperClass(tox_save.ToxSave):
|
|||||||
with open(path_to_data(self._short_name) + 'settings.json', 'wb') as fl:
|
with open(path_to_data(self._short_name) + 'settings.json', 'wb') as fl:
|
||||||
fl.write(bytes(data, 'utf-8'))
|
fl.write(bytes(data, 'utf-8'))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks
|
# Callbacks
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def lossless_packet(self, data, friend_number):
|
def lossless_packet(self, data, friend_number):
|
||||||
"""
|
"""
|
||||||
@ -186,7 +196,9 @@ class PluginSuperClass(tox_save.ToxSave):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Custom packets sending
|
# Custom packets sending
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_lossless(self, data, friend_number):
|
def send_lossless(self, data, friend_number):
|
||||||
"""
|
"""
|
||||||
|
Binary file not shown.
@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE TS>
|
|
||||||
<TS version="2.0" language="ru_RU">
|
|
||||||
<context>
|
|
||||||
<name>BirthDay</name>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="28"/>
|
|
||||||
<source>Birthday!</source>
|
|
||||||
<translation>День рождения!</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="44"/>
|
|
||||||
<source>Send my birthday date to contacts</source>
|
|
||||||
<translation>Отправлять дату моего рождения контактам</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="45"/>
|
|
||||||
<source>Birthday</source>
|
|
||||||
<translation>День рождения</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="50"/>
|
|
||||||
<source>Date in format dd.mm.yyyy</source>
|
|
||||||
<translation>Дата в формате дд.мм.гггг</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="bday.py" line="53"/>
|
|
||||||
<source>Save date</source>
|
|
||||||
<translation>Сохранить дату</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
</TS>
|
|
@ -1,2 +0,0 @@
|
|||||||
SOURCES = srch.py
|
|
||||||
TRANSLATIONS = srch/en_GB.ts srch/en_US.ts srch/ru_RU.ts
|
|
@ -1,54 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
||||||
|
|
||||||
|
|
||||||
class SearchPlugin(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(SearchPlugin, self).__init__('SearchPlugin', 'srch', *args)
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QApplication.translate("SearchPlugin", 'Plugin search with search engines.')
|
|
||||||
|
|
||||||
def get_message_menu(self, menu, text):
|
|
||||||
google = QtWidgets.QAction(
|
|
||||||
QtWidgets.QApplication.translate("srch", "Find in Google"),
|
|
||||||
menu)
|
|
||||||
google.triggered.connect(lambda: self.google(text))
|
|
||||||
|
|
||||||
duck = QtWidgets.QAction(
|
|
||||||
QtWidgets.QApplication.translate("srch", "Find in DuckDuckGo"),
|
|
||||||
menu)
|
|
||||||
duck.triggered.connect(lambda: self.duck(text))
|
|
||||||
|
|
||||||
yandex = QtWidgets.QAction(
|
|
||||||
QtWidgets.QApplication.translate("srch", "Find in Yandex"),
|
|
||||||
menu)
|
|
||||||
yandex.triggered.connect(lambda: self.yandex(text))
|
|
||||||
|
|
||||||
bing = QtWidgets.QAction(
|
|
||||||
QtWidgets.QApplication.translate("srch", "Find in Bing"),
|
|
||||||
menu)
|
|
||||||
bing.triggered.connect(lambda: self.bing(text))
|
|
||||||
|
|
||||||
return [duck, google, yandex, bing]
|
|
||||||
|
|
||||||
def google(self, text):
|
|
||||||
url = QtCore.QUrl('https://www.google.com/search?q=' + text)
|
|
||||||
self.open_url(url)
|
|
||||||
|
|
||||||
def duck(self, text):
|
|
||||||
url = QtCore.QUrl('https://duckduckgo.com/?q=' + text)
|
|
||||||
self.open_url(url)
|
|
||||||
|
|
||||||
def yandex(self, text):
|
|
||||||
url = QtCore.QUrl('https://yandex.com/search/?text=' + text)
|
|
||||||
self.open_url(url)
|
|
||||||
|
|
||||||
def bing(self, text):
|
|
||||||
url = QtCore.QUrl('https://www.bing.com/search?q=' + text)
|
|
||||||
self.open_url(url)
|
|
||||||
|
|
||||||
def open_url(self, url):
|
|
||||||
QtGui.QDesktopServices.openUrl(url)
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
SOURCES = toxid.py
|
|
||||||
TRANSLATIONS = toxid/en_GB.ts toxid/en_US.ts toxid/ru_RU.ts
|
|
@ -1,136 +0,0 @@
|
|||||||
import plugin_super_class
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class CopyableToxId(plugin_super_class.PluginSuperClass):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(CopyableToxId, self).__init__('CopyableToxId', 'toxid', *args)
|
|
||||||
self._data = json.loads(self.load_settings())
|
|
||||||
self._copy = False
|
|
||||||
self._curr = -1
|
|
||||||
self._timer = QtCore.QTimer()
|
|
||||||
self._timer.timeout.connect(lambda: self.timer())
|
|
||||||
self.load_translator()
|
|
||||||
self._app = args[0]
|
|
||||||
self._profile=self._app._ms._profile
|
|
||||||
self._window = None
|
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
return QtWidgets.QApplication.translate("TOXID", 'Plugin which allows you to copy TOX ID of your friends easily.')
|
|
||||||
|
|
||||||
def get_window(self):
|
|
||||||
inst = self
|
|
||||||
|
|
||||||
class Window(QtWidgets.QWidget):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Window, self).__init__()
|
|
||||||
self.setGeometry(QtCore.QRect(450, 300, 350, 100))
|
|
||||||
self.send = QtWidgets.QCheckBox(self)
|
|
||||||
self.send.setGeometry(QtCore.QRect(20, 10, 310, 25))
|
|
||||||
self.send.setText(QtWidgets.QApplication.translate("TOXID", "Send my TOX ID to contacts"))
|
|
||||||
self.setWindowTitle(QtWidgets.QApplication.translate("TOXID", "CopyableToxID"))
|
|
||||||
self.send.clicked.connect(self.update)
|
|
||||||
self.send.setChecked(inst._data['send_id'])
|
|
||||||
self.help = QtWidgets.QPushButton(self)
|
|
||||||
self.help.setGeometry(QtCore.QRect(20, 40, 200, 25))
|
|
||||||
self.help.setText(QtWidgets.QApplication.translate("TOXID", "List of commands"))
|
|
||||||
self.help.clicked.connect(lambda: inst.command('help'))
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
inst._data['send_id'] = self.send.isChecked()
|
|
||||||
inst.save_settings(json.dumps(inst._data))
|
|
||||||
|
|
||||||
if not hasattr(self, '_window') or not self._window:
|
|
||||||
self._window = Window()
|
|
||||||
return self._window
|
|
||||||
|
|
||||||
def lossless_packet(self, data, friend_number):
|
|
||||||
if len(data):
|
|
||||||
self._data['id'] = list(filter(lambda x: not x.startswith(data[:64]), self._data['id']))
|
|
||||||
self._data['id'].append(data)
|
|
||||||
if self._copy:
|
|
||||||
self._timer.stop()
|
|
||||||
self._copy = False
|
|
||||||
clipboard = QtWidgets.QApplication.clipboard()
|
|
||||||
clipboard.setText(data)
|
|
||||||
self.save_settings(json.dumps(self._data))
|
|
||||||
elif self._data['send_id']:
|
|
||||||
self.send_lossless(self._tox.self_get_address(), friend_number)
|
|
||||||
|
|
||||||
def error(self):
|
|
||||||
msgbox = QtWidgets.QMessageBox()
|
|
||||||
title = QtWidgets.QApplication.translate("TOXID", "Error")
|
|
||||||
msgbox.setWindowTitle(title.format(self._name))
|
|
||||||
text = QtWidgets.QApplication.translate("TOXID", "Tox ID cannot be copied")
|
|
||||||
msgbox.setText(text)
|
|
||||||
msgbox.exec_()
|
|
||||||
|
|
||||||
def timer(self):
|
|
||||||
self._copy = False
|
|
||||||
if self._curr + 1:
|
|
||||||
public_key = self._tox.friend_get_public_key(self._curr)
|
|
||||||
self._curr = -1
|
|
||||||
arr = list(filter(lambda x: x.startswith(public_key), self._data['id']))
|
|
||||||
if len(arr):
|
|
||||||
clipboard = QtWidgets.QApplication.clipboard()
|
|
||||||
clipboard.setText(arr[0])
|
|
||||||
else:
|
|
||||||
self.error()
|
|
||||||
else:
|
|
||||||
self.error()
|
|
||||||
self._timer.stop()
|
|
||||||
|
|
||||||
def friend_connected(self, friend_number):
|
|
||||||
self.send_lossless('', friend_number)
|
|
||||||
|
|
||||||
def command(self, text):
|
|
||||||
if text == 'copy':
|
|
||||||
num = self._profile.get_active_number()
|
|
||||||
if num == -1:
|
|
||||||
return
|
|
||||||
elif text.startswith('copy '):
|
|
||||||
num = int(text[5:])
|
|
||||||
if num < 0:
|
|
||||||
return
|
|
||||||
elif text == 'enable':
|
|
||||||
self._copy = True
|
|
||||||
return
|
|
||||||
elif text == 'disable':
|
|
||||||
self._copy = False
|
|
||||||
return
|
|
||||||
elif text == 'help':
|
|
||||||
msgbox = QtWidgets.QMessageBox()
|
|
||||||
title = QtWidgets.QApplication.translate("TOXID", "List of commands for plugin CopyableToxID")
|
|
||||||
msgbox.setWindowTitle(title)
|
|
||||||
text = QtWidgets.QApplication.translate("TOXID", """Commands:
|
|
||||||
copy: copy TOX ID of current friend
|
|
||||||
copy <friend_number>: copy TOX ID of friend with specified number
|
|
||||||
enable: allow send your TOX ID to friends
|
|
||||||
disable: disallow send your TOX ID to friends
|
|
||||||
help: show this help""")
|
|
||||||
msgbox.setText(text)
|
|
||||||
msgbox.exec_()
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
public_key = self._tox.friend_get_public_key(num)
|
|
||||||
arr = list(filter(lambda x: x.startswith(public_key), self._data['id']))
|
|
||||||
if self._profile.get_friend_by_number(num).status is None and len(arr):
|
|
||||||
clipboard = QtWidgets.QApplication.clipboard()
|
|
||||||
clipboard.setText(arr[0])
|
|
||||||
elif self._profile.get_friend_by_number(num).status is not None:
|
|
||||||
self._copy = True
|
|
||||||
self._curr = num
|
|
||||||
self.send_lossless('', num)
|
|
||||||
self._timer.start(2000)
|
|
||||||
else:
|
|
||||||
self.error()
|
|
||||||
|
|
||||||
def get_menu(self, menu, num):
|
|
||||||
act = QtWidgets.QAction(QtWidgets.QApplication.translate("TOXID", "Copy TOX ID"), menu)
|
|
||||||
friend = self._profile.get_friend(num)
|
|
||||||
act.connect(act, QtCore.SIGNAL("triggered()"), lambda: self.command('copy ' + str(friend.number)))
|
|
||||||
return [act]
|
|
@ -1,87 +0,0 @@
|
|||||||
# toxygen_wrapper
|
|
||||||
|
|
||||||
[ctypes](https://docs.python.org/3/library/ctypes.html)
|
|
||||||
wrapping of [Tox](https://tox.chat/)
|
|
||||||
[```libtoxcore```](https://github.com/TokTok/c-toxcore) into Python.
|
|
||||||
Taken from the ```wrapper``` directory of the now abandoned
|
|
||||||
<https://github.com/toxygen-project/toxygen> ```next_gen``` branch
|
|
||||||
by Ingvar.
|
|
||||||
|
|
||||||
The basics of NGC groups are supported, as well as AV and toxencryptsave.
|
|
||||||
There is no coverage of conferences as they are not used in ```toxygen```
|
|
||||||
and the list of still unwrapped calls as of Sept. 2022 can be found in
|
|
||||||
```tox.c-toxcore.missing```. The code still needs double-checking
|
|
||||||
that every call in ```tox.py``` has the right signature, but it runs
|
|
||||||
```toxygen``` with no apparent issues.
|
|
||||||
|
|
||||||
It has been tested with UDP and TCP proxy (Tor). It has ***not*** been
|
|
||||||
tested on Windows, and there may be some minor breakage, which should be
|
|
||||||
easy to fix. There is a good coverage integration testsuite in ```wrapper_tests```.
|
|
||||||
Change to that directory and run ```tests_wrapper.py --help```; the test
|
|
||||||
suite gives a good set of examples of usage.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
Put the parent of the wrapper directory on your PYTHONPATH and
|
|
||||||
touch a file called `__init__.py` in its parent directory.
|
|
||||||
|
|
||||||
Then you need a ```libs``` directory beside the `wrapper` directory
|
|
||||||
and you need to link your ```libtoxcore.so``` and ```libtoxav.so```
|
|
||||||
and ```libtoxencryptsave.so``` into it. Link all 3 filenames
|
|
||||||
to ```libtoxcore.so``` if you have only ```libtoxcore.so```
|
|
||||||
(which is usually the case if you built ```c-toxcore``` with ```cmake```
|
|
||||||
rather than ```autogen/configure```). If you want to be different,
|
|
||||||
the environment variable TOXCORE_LIBS overrides the location of ```libs```.
|
|
||||||
|
|
||||||
As is, the code in ```tox.py``` is very verbose. Edit the file to change
|
|
||||||
```
|
|
||||||
def LOG_ERROR(a): print('EROR> '+a)
|
|
||||||
def LOG_WARN(a): print('WARN> '+a)
|
|
||||||
def LOG_INFO(a): print('INFO> '+a)
|
|
||||||
def LOG_DEBUG(a): print('DBUG> '+a)
|
|
||||||
def LOG_TRACE(a): pass # print('TRAC> '+a)
|
|
||||||
```
|
|
||||||
to all ```pass #``` or use ```logging.logger``` to suite your tastes.
|
|
||||||
```logging.logger``` can be dangerous in callbacks in ```Qt``` applications,
|
|
||||||
so we use simple print statements as default. The same applies to
|
|
||||||
```wrapper/tests_wrapper.py```.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
No prerequisites in Python3.
|
|
||||||
|
|
||||||
## Other wrappers
|
|
||||||
|
|
||||||
There are a number of other wrappings into Python of Tox core.
|
|
||||||
This one uses [ctypes](https://docs.python.org/3/library/ctypes.html)
|
|
||||||
which has its merits - there is no need to recompile anything as with
|
|
||||||
Cython - change the Python file and it's done. And you can follow things
|
|
||||||
in a Python debugger, or with the utterly stupendous Python feature of
|
|
||||||
```gdb``` (```gdb -ex r --args /usr/bin/python3.9 <pyfile>```).
|
|
||||||
|
|
||||||
CTYPES code can be brittle, segfaulting if you've got things wrong,
|
|
||||||
but if your wrapping is right, it is very efficient and easy to work on.
|
|
||||||
The [faulthandler](https://docs.python.org/3/library/faulthandler.html)
|
|
||||||
module can be helpful in debugging crashes
|
|
||||||
(e.g. from segmentation faults produced by erroneous C library wrapping).
|
|
||||||
|
|
||||||
Others include:
|
|
||||||
|
|
||||||
* <https://github.com/TokTok/py-toxcore-c> Cython bindings.
|
|
||||||
Incomplete and not really actively supported. Maybe it will get
|
|
||||||
worked on in the future, but TokTok seems to be working on
|
|
||||||
java, rust, scalla, go, etc. bindings instead.
|
|
||||||
No support for NGC groups or toxencryptsave.
|
|
||||||
|
|
||||||
* <https://github.com/oxij/PyTox>
|
|
||||||
forked from https://github.com/aitjcize/PyTox
|
|
||||||
by Wei-Ning Huang <aitjcize@gmail.com>.
|
|
||||||
Hardcore C wrapping which is not easy to keep up to date.
|
|
||||||
No support for NGC or toxencryptsave. Abandonned.
|
|
||||||
This was the basis for the TokTok/py-toxcore-c code until recently.
|
|
||||||
|
|
||||||
To our point of view, the ability of CTYPEs to follow code in the
|
|
||||||
debugger is a crucial advantage.
|
|
||||||
|
|
||||||
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!
|
|
@ -1,151 +0,0 @@
|
|||||||
if False:
|
|
||||||
@unittest.skip # to yet
|
|
||||||
def test_conference(self):
|
|
||||||
"""
|
|
||||||
t:group_new
|
|
||||||
t:conference_delete
|
|
||||||
t:conference_get_chatlist_size
|
|
||||||
t:conference_get_chatlist
|
|
||||||
t:conference_send_message
|
|
||||||
"""
|
|
||||||
bob_addr = self.bob.self_get_address()
|
|
||||||
alice_addr = self.alice.self_get_address()
|
|
||||||
|
|
||||||
self.abid = self.alice.friend_by_public_key(bob_addr)
|
|
||||||
self.baid = self.bob.friend_by_public_key(alice_addr)
|
|
||||||
|
|
||||||
assert self.bob_just_add_alice_as_friend()
|
|
||||||
|
|
||||||
#: Test group add
|
|
||||||
privacy_state = enums.TOX_GROUP_PRIVACY_STATE['PUBLIC']
|
|
||||||
group_name = 'test_group'
|
|
||||||
nick = 'test_nick'
|
|
||||||
status = None # dunno
|
|
||||||
self.group_id = self.bob.group_new(privacy_state, group_name, nick, status)
|
|
||||||
# :return group number on success, UINT32_MAX on failure.
|
|
||||||
assert self.group_id >= 0
|
|
||||||
|
|
||||||
self.loop(50)
|
|
||||||
|
|
||||||
BID = self.abid
|
|
||||||
|
|
||||||
def alices_on_conference_invite(self, fid, type_, data):
|
|
||||||
assert fid == BID
|
|
||||||
assert type_ == 0
|
|
||||||
gn = self.conference_join(fid, data)
|
|
||||||
assert type_ == self.conference_get_type(gn)
|
|
||||||
self.gi = True
|
|
||||||
|
|
||||||
def alices_on_conference_peer_list_changed(self, gid):
|
|
||||||
logging.debug("alices_on_conference_peer_list_changed")
|
|
||||||
assert gid == self.group_id
|
|
||||||
self.gn = True
|
|
||||||
|
|
||||||
try:
|
|
||||||
AliceTox.on_conference_invite = alices_on_conference_invite
|
|
||||||
AliceTox.on_conference_peer_list_changed = alices_on_conference_peer_list_changed
|
|
||||||
|
|
||||||
self.alice.gi = False
|
|
||||||
self.alice.gn = False
|
|
||||||
|
|
||||||
self.wait_ensure_exec(self.bob.conference_invite, (self.aid, self.group_id))
|
|
||||||
|
|
||||||
assert self.wait_callback_trues(self.alice, ['gi', 'gn'])
|
|
||||||
except AssertionError as e:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
AliceTox.on_conference_invite = Tox.on_conference_invite
|
|
||||||
AliceTox.on_conference_peer_list_change = Tox.on_conference_peer_list_changed
|
|
||||||
|
|
||||||
#: Test group number of peers
|
|
||||||
self.loop(50)
|
|
||||||
assert self.bob.conference_peer_count(self.group_id) == 2
|
|
||||||
|
|
||||||
#: Test group peername
|
|
||||||
self.alice.self_set_name('Alice')
|
|
||||||
self.bob.self_set_name('Bob')
|
|
||||||
|
|
||||||
def alices_on_conference_peer_list_changed(self, gid):
|
|
||||||
logging.debug("alices_on_conference_peer_list_changed")
|
|
||||||
self.gn = True
|
|
||||||
try:
|
|
||||||
AliceTox.on_conference_peer_list_changed = alices_on_conference_peer_list_changed
|
|
||||||
self.alice.gn = False
|
|
||||||
|
|
||||||
assert self.wait_callback_true(self.alice, 'gn')
|
|
||||||
except AssertionError as e:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
AliceTox.on_conference_peer_list_changed = Tox.on_conference_peer_list_changed
|
|
||||||
|
|
||||||
peernames = [self.bob.conference_peer_get_name(self.group_id, i) for i in
|
|
||||||
range(self.bob.conference_peer_count(self.group_id))]
|
|
||||||
assert 'Alice' in peernames
|
|
||||||
assert 'Bob' in peernames
|
|
||||||
|
|
||||||
#: Test title change
|
|
||||||
self.bob.conference_set_title(self.group_id, 'My special title')
|
|
||||||
assert self.bob.conference_get_title(self.group_id) == 'My special title'
|
|
||||||
|
|
||||||
#: Test group message
|
|
||||||
AID = self.aid
|
|
||||||
BID = self.bid
|
|
||||||
MSG = 'Group message test'
|
|
||||||
|
|
||||||
def alices_on_conference_message(self, gid, fgid, msg_type, message):
|
|
||||||
logging.debug("alices_on_conference_message" +repr(message))
|
|
||||||
if fgid == AID:
|
|
||||||
assert gid == self.group_id
|
|
||||||
assert str(message, 'UTF-8') == MSG
|
|
||||||
self.alice.gm = True
|
|
||||||
|
|
||||||
try:
|
|
||||||
AliceTox.on_conference_message = alices_on_conference_message
|
|
||||||
self.alice.gm = False
|
|
||||||
|
|
||||||
self.wait_ensure_exec(self.bob.conference_send_message, (
|
|
||||||
self.group_id, TOX_MESSAGE_TYPE['NORMAL'], MSG))
|
|
||||||
assert self.wait_callback_true(self.alice, 'gm')
|
|
||||||
except AssertionError as e:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
AliceTox.on_conference_message = Tox.on_conference_message
|
|
||||||
|
|
||||||
#: Test group action
|
|
||||||
AID = self.aid
|
|
||||||
BID = self.bid
|
|
||||||
MSG = 'Group action test'
|
|
||||||
|
|
||||||
def on_conference_action(self, gid, fgid, msg_type, action):
|
|
||||||
if fgid == AID:
|
|
||||||
assert gid == self.group_id
|
|
||||||
assert msg_type == TOX_MESSAGE_TYPE['ACTION']
|
|
||||||
assert str(action, 'UTF-8') == MSG
|
|
||||||
self.ga = True
|
|
||||||
|
|
||||||
try:
|
|
||||||
AliceTox.on_conference_message = on_conference_action
|
|
||||||
self.alice.ga = False
|
|
||||||
|
|
||||||
self.wait_ensure_exec(self.bob.conference_send_message,
|
|
||||||
(self.group_id, TOX_MESSAGE_TYPE['ACTION'], MSG))
|
|
||||||
|
|
||||||
assert self.wait_callback_true(self.alice, 'ga')
|
|
||||||
|
|
||||||
#: Test chatlist
|
|
||||||
assert len(self.bob.conference_get_chatlist()) == self.bob.conference_get_chatlist_size(), \
|
|
||||||
print(len(self.bob.conference_get_chatlist()), '!=', self.bob.conference_get_chatlist_size())
|
|
||||||
assert len(self.alice.conference_get_chatlist()) == self.bob.conference_get_chatlist_size(), \
|
|
||||||
print(len(self.alice.conference_get_chatlist()), '!=', self.bob.conference_get_chatlist_size())
|
|
||||||
assert self.bob.conference_get_chatlist_size() == 1, \
|
|
||||||
self.bob.conference_get_chatlist_size()
|
|
||||||
self.bob.conference_delete(self.group_id)
|
|
||||||
assert self.bob.conference_get_chatlist_size() == 0, \
|
|
||||||
self.bob.conference_get_chatlist_size()
|
|
||||||
|
|
||||||
except AssertionError as e:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
AliceTox.on_conference_message = Tox.on_conference_message
|
|
||||||
|
|
||||||
|
|
@ -1,439 +0,0 @@
|
|||||||
#!/var/local/bin/python3.bash
|
|
||||||
#
|
|
||||||
""" echo.py features
|
|
||||||
- accept friend request
|
|
||||||
- echo back friend message
|
|
||||||
- accept and answer friend call request
|
|
||||||
- send back friend audio/video data
|
|
||||||
- send back files friend sent
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import traceback
|
|
||||||
import random
|
|
||||||
from ctypes import *
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
import time
|
|
||||||
from os.path import exists
|
|
||||||
|
|
||||||
# LOG=util.log
|
|
||||||
global LOG
|
|
||||||
import logging
|
|
||||||
# log = lambda x: LOG.info(x)
|
|
||||||
LOG = logging.getLogger('app')
|
|
||||||
def LOG_error(a): print('EROR_ '+a)
|
|
||||||
def LOG_warn(a): print('WARN_ '+a)
|
|
||||||
def LOG_info(a): print('INFO_ '+a)
|
|
||||||
def LOG_debug(a): print('DBUG_ '+a)
|
|
||||||
def LOG_trace(a): pass # print('TRAC_ '+a)
|
|
||||||
|
|
||||||
from middleware.tox_factory import tox_factory
|
|
||||||
import wrapper
|
|
||||||
import wrapper.toxcore_enums_and_consts as enums
|
|
||||||
from wrapper.toxcore_enums_and_consts import TOX_CONNECTION, TOX_USER_STATUS, \
|
|
||||||
TOX_MESSAGE_TYPE, TOX_PUBLIC_KEY_SIZE, TOX_FILE_CONTROL
|
|
||||||
import user_data
|
|
||||||
from wrapper.libtox import LibToxCore
|
|
||||||
import wrapper_tests.support_testing as ts
|
|
||||||
from wrapper_tests.support_testing import oMainArgparser
|
|
||||||
from wrapper_tests.support_testing import logging_toxygen_echo
|
|
||||||
|
|
||||||
def sleep(fSec):
|
|
||||||
if 'QtCore' in globals():
|
|
||||||
if fSec > .000001: globals['QtCore'].QThread.msleep(fSec)
|
|
||||||
globals['QtCore'].QCoreApplication.processEvents()
|
|
||||||
else:
|
|
||||||
time.sleep(fSec)
|
|
||||||
|
|
||||||
try:
|
|
||||||
import coloredlogs
|
|
||||||
if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ:
|
|
||||||
os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red'
|
|
||||||
except ImportError as e:
|
|
||||||
# logging.log(logging.DEBUG, f"coloredlogs not available: {e}")
|
|
||||||
coloredlogs = None
|
|
||||||
|
|
||||||
import wrapper_tests.support_testing as ts
|
|
||||||
if 'USER' in os.environ:
|
|
||||||
sDATA_FILE = '/tmp/logging_toxygen_' +os.environ['USER'] +'.tox'
|
|
||||||
elif 'USERNAME' in os.environ:
|
|
||||||
sDATA_FILE = '/tmp/logging_toxygen_' +os.environ['USERNAME'] +'.tox'
|
|
||||||
else:
|
|
||||||
sDATA_FILE = '/tmp/logging_toxygen_' +'data' +'.tox'
|
|
||||||
|
|
||||||
bHAVE_AV = True
|
|
||||||
iDHT_TRIES = 100
|
|
||||||
iDHT_TRY = 0
|
|
||||||
|
|
||||||
#?SERVER = lLOCAL[-1]
|
|
||||||
|
|
||||||
class AV(wrapper.tox.ToxAV):
|
|
||||||
def __init__(self, core):
|
|
||||||
super(AV, self).__init__(core)
|
|
||||||
self.core = self.get_tox()
|
|
||||||
|
|
||||||
def on_call(self, fid, audio_enabled, video_enabled):
|
|
||||||
LOG.info("Incoming %s call from %d:%s ..." % (
|
|
||||||
"video" if video_enabled else "audio", fid,
|
|
||||||
self.core.friend_get_name(fid)))
|
|
||||||
bret = self.answer(fid, 48, 64)
|
|
||||||
LOG.info(f"Answered, in call... {bret!s}")
|
|
||||||
|
|
||||||
def on_call_state(self, fid, state):
|
|
||||||
LOG.info('call state:fn=%d, state=%d' % (fid, state))
|
|
||||||
|
|
||||||
def on_audio_bit_rate(self, fid, audio_bit_rate):
|
|
||||||
LOG.info('audio bit rate status: fn=%d, abr=%d' %
|
|
||||||
(fid, audio_bit_rate))
|
|
||||||
|
|
||||||
def on_video_bit_rate(self, fid, video_bit_rate):
|
|
||||||
LOG.info('video bit rate status: fn=%d, vbr=%d' %
|
|
||||||
(fid, video_bit_rate))
|
|
||||||
|
|
||||||
def on_audio_receive_frame(self, fid, pcm, sample_count,
|
|
||||||
channels, sampling_rate):
|
|
||||||
# LOG.info('audio frame: %d, %d, %d, %d' %
|
|
||||||
# (fid, sample_count, channels, sampling_rate))
|
|
||||||
# LOG.info('pcm len:%d, %s' % (len(pcm), str(type(pcm))))
|
|
||||||
sys.stdout.write('.')
|
|
||||||
sys.stdout.flush()
|
|
||||||
bret = self.audio_send_frame(fid, pcm, sample_count,
|
|
||||||
channels, sampling_rate)
|
|
||||||
if bret is False:
|
|
||||||
LOG.error('on_audio_receive_frame error.')
|
|
||||||
|
|
||||||
def on_video_receive_frame(self, fid, width, height, frame, u, v):
|
|
||||||
LOG.info('video frame: %d, %d, %d, ' % (fid, width, height))
|
|
||||||
sys.stdout.write('*')
|
|
||||||
sys.stdout.flush()
|
|
||||||
bret = self.video_send_frame(fid, width, height, frame, u, v)
|
|
||||||
if bret is False:
|
|
||||||
LOG.error('on_video_receive_frame error.')
|
|
||||||
|
|
||||||
def witerate(self):
|
|
||||||
self.iterate()
|
|
||||||
|
|
||||||
|
|
||||||
def save_to_file(tox, fname):
|
|
||||||
data = tox.get_savedata()
|
|
||||||
with open(fname, 'wb') as f:
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
def load_from_file(fname):
|
|
||||||
assert os.path.exists(fname)
|
|
||||||
return open(fname, 'rb').read()
|
|
||||||
|
|
||||||
class EchoBot():
|
|
||||||
def __init__(self, oTox):
|
|
||||||
self._tox = oTox
|
|
||||||
self._tox.self_set_name("EchoBot")
|
|
||||||
LOG.info('ID: %s' % self._tox.self_get_address())
|
|
||||||
|
|
||||||
self.files = {}
|
|
||||||
self.av = None
|
|
||||||
self.on_connection_status = None
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self.connect()
|
|
||||||
if bHAVE_AV:
|
|
||||||
# RuntimeError: Attempted to create a second session for the same Tox instance.
|
|
||||||
|
|
||||||
self.av = True # AV(self._tox_pointer)
|
|
||||||
def bobs_on_friend_request(iTox,
|
|
||||||
public_key,
|
|
||||||
message_data,
|
|
||||||
message_data_size,
|
|
||||||
*largs):
|
|
||||||
key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE])
|
|
||||||
sPk = wrapper.tox.bin_to_string(key, TOX_PUBLIC_KEY_SIZE)
|
|
||||||
sMd = str(message_data, 'UTF-8')
|
|
||||||
LOG.debug('on_friend_request ' +sPk +' ' +sMd)
|
|
||||||
self.on_friend_request(sPk, sMd)
|
|
||||||
LOG.info('setting bobs_on_friend_request')
|
|
||||||
self._tox.callback_friend_request(bobs_on_friend_request)
|
|
||||||
|
|
||||||
def bobs_on_friend_message(iTox,
|
|
||||||
iFriendNum,
|
|
||||||
iMessageType,
|
|
||||||
message_data,
|
|
||||||
message_data_size,
|
|
||||||
*largs):
|
|
||||||
sMd = str(message_data, 'UTF-8')
|
|
||||||
LOG_debug(f"on_friend_message {iFriendNum}" +' ' +sMd)
|
|
||||||
self.on_friend_message(iFriendNum, iMessageType, sMd)
|
|
||||||
LOG.info('setting bobs_on_friend_message')
|
|
||||||
self._tox.callback_friend_message(bobs_on_friend_message)
|
|
||||||
|
|
||||||
def bobs_on_file_chunk_request(iTox, fid, filenumber, position, length, *largs):
|
|
||||||
if length == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
data = self.files[(fid, filenumber)]['f'][position:(position + length)]
|
|
||||||
self._tox.file_send_chunk(fid, filenumber, position, data)
|
|
||||||
self._tox.callback_file_chunk_request(bobs_on_file_chunk_request)
|
|
||||||
|
|
||||||
def bobs_on_file_recv(iTox, fid, filenumber, kind, size, filename, *largs):
|
|
||||||
LOG_info(f"on_file_recv {fid!s} {filenumber!s} {kind!s} {size!s} {filename}")
|
|
||||||
if size == 0:
|
|
||||||
return
|
|
||||||
self.files[(fid, filenumber)] = {
|
|
||||||
'f': bytes(),
|
|
||||||
'filename': filename,
|
|
||||||
'size': size
|
|
||||||
}
|
|
||||||
self._tox.file_control(fid, filenumber, TOX_FILE_CONTROL['RESUME'])
|
|
||||||
|
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
if not self.on_connection_status:
|
|
||||||
def on_connection_status(iTox, iCon, *largs):
|
|
||||||
LOG_info('ON_CONNECTION_STATUS - CONNECTED ' + repr(iCon))
|
|
||||||
self._tox.callback_self_connection_status(on_connection_status)
|
|
||||||
LOG.info('setting on_connection_status callback ')
|
|
||||||
self.on_connection_status = on_connection_status
|
|
||||||
if self._oargs.network in ['newlocal', 'local']:
|
|
||||||
LOG.info('connecting on the new network ')
|
|
||||||
sNet = 'newlocal'
|
|
||||||
elif self._oargs.network == 'new':
|
|
||||||
LOG.info('connecting on the new network ')
|
|
||||||
sNet = 'new'
|
|
||||||
else: # main old
|
|
||||||
LOG.info('connecting on the old network ')
|
|
||||||
sNet = 'old'
|
|
||||||
sFile = self._oargs.nodes_json
|
|
||||||
lNodes = generate_nodes_from_file(sFile)
|
|
||||||
lElts = lNodes
|
|
||||||
random.shuffle(lElts)
|
|
||||||
for lElt in lElts[:10]:
|
|
||||||
status = self._tox.self_get_connection_status()
|
|
||||||
try:
|
|
||||||
if self._tox.bootstrap(*lElt):
|
|
||||||
LOG.info('connected to ' + lElt[0]+' '+repr(status))
|
|
||||||
else:
|
|
||||||
LOG.warn('failed connecting to ' + lElt[0])
|
|
||||||
except Exception as e:
|
|
||||||
LOG.warn('error connecting to ' + lElt[0])
|
|
||||||
|
|
||||||
if self._oargs.proxy_type > 0:
|
|
||||||
random.shuffle(ts.lRELAYS)
|
|
||||||
for lElt in ts.lRELAYS[:10]:
|
|
||||||
status = self._tox.self_get_connection_status()
|
|
||||||
try:
|
|
||||||
if self._tox.add_tcp_relay(*lElt):
|
|
||||||
LOG.info('relayed to ' + lElt[0] +' '+repr(status))
|
|
||||||
else:
|
|
||||||
LOG.warn('failed relay to ' + lElt[0])
|
|
||||||
except Exception as e:
|
|
||||||
LOG.warn('error relay to ' + lElt[0])
|
|
||||||
|
|
||||||
def loop(self):
|
|
||||||
if not self.av:
|
|
||||||
self.start()
|
|
||||||
checked = False
|
|
||||||
save_to_file(self._tox, sDATA_FILE)
|
|
||||||
|
|
||||||
LOG.info('Starting loop.')
|
|
||||||
while True:
|
|
||||||
|
|
||||||
status = self._tox.self_get_connection_status()
|
|
||||||
if not checked and status:
|
|
||||||
LOG.info('Connected to DHT.')
|
|
||||||
checked = True
|
|
||||||
if not checked and not status:
|
|
||||||
global iDHT_TRY
|
|
||||||
iDHT_TRY += 10
|
|
||||||
self.connect()
|
|
||||||
self.iterate(100)
|
|
||||||
if iDHT_TRY >= iDHT_TRIES:
|
|
||||||
raise RuntimeError("Failed to connect to the DHT.")
|
|
||||||
LOG.warn(f"NOT Connected to DHT. {iDHT_TRY}")
|
|
||||||
checked = True
|
|
||||||
if checked and not status:
|
|
||||||
LOG.info('Disconnected from DHT.')
|
|
||||||
self.connect()
|
|
||||||
checked = False
|
|
||||||
|
|
||||||
if bHAVE_AV:
|
|
||||||
True # self.av.witerate()
|
|
||||||
self.iterate(100)
|
|
||||||
|
|
||||||
LOG.info('Ending loop.')
|
|
||||||
|
|
||||||
def iterate(self, n=100):
|
|
||||||
interval = self._tox.iteration_interval()
|
|
||||||
for i in range(n):
|
|
||||||
self._tox.iterate()
|
|
||||||
sleep(interval / 1000.0)
|
|
||||||
self._tox.iterate()
|
|
||||||
|
|
||||||
def on_friend_request(self, pk, message):
|
|
||||||
LOG.debug('Friend request from %s: %s' % (pk, message))
|
|
||||||
self._tox.friend_add_norequest(pk)
|
|
||||||
LOG.info('on_friend_request Accepted.')
|
|
||||||
save_to_file(self._tox, sDATA_FILE)
|
|
||||||
|
|
||||||
def on_friend_message(self, friendId, type, message):
|
|
||||||
name = self._tox.friend_get_name(friendId)
|
|
||||||
LOG.debug('%s: %s' % (name, message))
|
|
||||||
yMessage = bytes(message, 'UTF-8')
|
|
||||||
self._tox.friend_send_message(friendId, TOX_MESSAGE_TYPE['NORMAL'], yMessage)
|
|
||||||
LOG.info('EchoBot sent: %s' % message)
|
|
||||||
|
|
||||||
def on_file_recv_chunk(self, fid, filenumber, position, data):
|
|
||||||
filename = self.files[(fid, filenumber)]['filename']
|
|
||||||
size = self.files[(fid, filenumber)]['size']
|
|
||||||
LOG.debug(f"on_file_recv_chunk {fid!s} {filenumber!s} {filename} {position/float(size)*100!s}")
|
|
||||||
|
|
||||||
if data is None:
|
|
||||||
msg = "I got '{}', sending it back right away!".format(filename)
|
|
||||||
self._tox.friend_send_message(fid, TOX_MESSAGE_TYPE['NORMAL'], msg)
|
|
||||||
|
|
||||||
self.files[(fid, 0)] = self.files[(fid, filenumber)]
|
|
||||||
|
|
||||||
length = self.files[(fid, filenumber)]['size']
|
|
||||||
self.file_send(fid, 0, length, filename, filename)
|
|
||||||
|
|
||||||
del self.files[(fid, filenumber)]
|
|
||||||
return
|
|
||||||
|
|
||||||
self.files[(fid, filenumber)]['f'] += data
|
|
||||||
|
|
||||||
def iMain(oArgs):
|
|
||||||
global sDATA_FILE
|
|
||||||
# oTOX_OPTIONS = ToxOptions()
|
|
||||||
global oTOX_OPTIONS
|
|
||||||
oTOX_OPTIONS = oToxygenToxOptions(oArgs)
|
|
||||||
opts = oTOX_OPTIONS
|
|
||||||
if coloredlogs:
|
|
||||||
coloredlogs.install(
|
|
||||||
level=oArgs.loglevel,
|
|
||||||
logger=LOG,
|
|
||||||
# %(asctime)s,%(msecs)03d %(hostname)s [%(process)d]
|
|
||||||
fmt='%(name)s %(levelname)s %(message)s'
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if 'logfile' in oArgs:
|
|
||||||
logging.basicConfig(filename=oArgs.logfile,
|
|
||||||
level=oArgs.loglevel,
|
|
||||||
format='%(levelname)-8s %(message)s')
|
|
||||||
else:
|
|
||||||
logging.basicConfig(level=oArgs.loglevel,
|
|
||||||
format='%(levelname)-8s %(message)s')
|
|
||||||
|
|
||||||
iRet = 0
|
|
||||||
if hasattr(oArgs,'profile') and oArgs.profile and os.path.isfile(oArgs.profile):
|
|
||||||
sDATA_FILE = oArgs.profile
|
|
||||||
LOG.info(f"loading from {sDATA_FILE}")
|
|
||||||
opts.savedata_data = load_from_file(sDATA_FILE)
|
|
||||||
opts.savedata_length = len(opts.savedata_data)
|
|
||||||
opts.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE']
|
|
||||||
else:
|
|
||||||
opts.savedata_data = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
if False:
|
|
||||||
oTox = tox_factory(data=opts.savedata_data,
|
|
||||||
settings=opts, args=oArgs, app=None)
|
|
||||||
else:
|
|
||||||
oTox = wrapper.tox.Tox(opts)
|
|
||||||
t = EchoBot(oTox)
|
|
||||||
t._oargs = oArgs
|
|
||||||
t.start()
|
|
||||||
t.loop()
|
|
||||||
save_to_file(t._tox, sDATA_FILE)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
save_to_file(t._tox, sDATA_FILE)
|
|
||||||
except RuntimeError as e:
|
|
||||||
LOG.error(f"exiting with {e}")
|
|
||||||
iRet = 1
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(f"exiting with {e}")
|
|
||||||
LOG.warn(' iMain(): ' \
|
|
||||||
+'\n' + traceback.format_exc())
|
|
||||||
iRet = 1
|
|
||||||
return iRet
|
|
||||||
|
|
||||||
def oToxygenToxOptions(oArgs, data=None):
|
|
||||||
tox_options = wrapper.tox.Tox.options_new()
|
|
||||||
|
|
||||||
tox_options.contents.local_discovery_enabled = False
|
|
||||||
tox_options.contents.dht_announcements_enabled = False
|
|
||||||
tox_options.contents.hole_punching_enabled = False
|
|
||||||
tox_options.contents.experimental_thread_safety = False
|
|
||||||
tox_options.contents.ipv6_enabled = False
|
|
||||||
tox_options.contents.tcp_port = 3390
|
|
||||||
|
|
||||||
if oArgs.proxy_type > 0:
|
|
||||||
tox_options.contents.proxy_type = int(oArgs.proxy_type)
|
|
||||||
tox_options.contents.proxy_host = bytes(oArgs.proxy_host, 'UTF-8')
|
|
||||||
tox_options.contents.proxy_port = int(oArgs.proxy_port)
|
|
||||||
tox_options.contents.udp_enabled = False
|
|
||||||
LOG.debug('setting oArgs.proxy_host = ' +oArgs.proxy_host)
|
|
||||||
else:
|
|
||||||
tox_options.contents.udp_enabled = True
|
|
||||||
|
|
||||||
if data: # load existing profile
|
|
||||||
tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE']
|
|
||||||
tox_options.contents.savedata_data = c_char_p(data)
|
|
||||||
tox_options.contents.savedata_length = len(data)
|
|
||||||
else: # create new profile
|
|
||||||
tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['NONE']
|
|
||||||
tox_options.contents.savedata_data = None
|
|
||||||
tox_options.contents.savedata_length = 0
|
|
||||||
|
|
||||||
if tox_options._options_pointer:
|
|
||||||
ts.vAddLoggerCallback(tox_options, ts.on_log)
|
|
||||||
else:
|
|
||||||
logging.warn("No tox_options._options_pointer " +repr(tox_options._options_pointer))
|
|
||||||
|
|
||||||
return tox_options
|
|
||||||
|
|
||||||
def oArgparse(lArgv):
|
|
||||||
parser = ts.oMainArgparser()
|
|
||||||
parser.add_argument('profile', type=str, nargs='?',
|
|
||||||
default=sDATA_FILE,
|
|
||||||
help='Path to Tox profile to save')
|
|
||||||
oArgs = parser.parse_args(lArgv)
|
|
||||||
if hasattr(oArgs, 'sleep') and oArgs.sleep == 'qt':
|
|
||||||
pass # broken
|
|
||||||
else:
|
|
||||||
oArgs.sleep = 'time'
|
|
||||||
for key in ts.lBOOLEANS:
|
|
||||||
if key not in oArgs: continue
|
|
||||||
val = getattr(oArgs, key)
|
|
||||||
if val in ['False', 'false', 0]:
|
|
||||||
setattr(oArgs, key, False)
|
|
||||||
else:
|
|
||||||
setattr(oArgs, key, True)
|
|
||||||
if not os.path.exists('/proc/sys/net/ipv6') and oArgs.ipv6_enabled:
|
|
||||||
LOG.warn('setting oArgs.ipv6_enabled = False')
|
|
||||||
oArgs.ipv6_enabled = False
|
|
||||||
return oArgs
|
|
||||||
|
|
||||||
def main(largs=None):
|
|
||||||
if largs is None: largs = []
|
|
||||||
oArgs = oArgparse(largs)
|
|
||||||
global oTOX_OARGS
|
|
||||||
oTOX_OARGS = oArgs
|
|
||||||
print(oArgs)
|
|
||||||
|
|
||||||
if coloredlogs:
|
|
||||||
logger = logging.getLogger()
|
|
||||||
# https://pypi.org/project/coloredlogs/
|
|
||||||
coloredlogs.install(level=oArgs.loglevel,
|
|
||||||
logger=logger,
|
|
||||||
# %(asctime)s,%(msecs)03d %(hostname)s [%(process)d]
|
|
||||||
fmt='%(name)s %(levelname)s %(message)s'
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(level=oArgs.loglevel) # logging.INFO
|
|
||||||
|
|
||||||
return iMain(oArgs)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main(sys.argv[1:]))
|
|
@ -1,391 +0,0 @@
|
|||||||
"""SocksiPy - Python SOCKS module.
|
|
||||||
Version 1.00
|
|
||||||
|
|
||||||
Copyright 2006 Dan-Haim. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
3. Neither the name of Dan Haim nor the names of his contributors may be used
|
|
||||||
to endorse or promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
||||||
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
||||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
|
|
||||||
|
|
||||||
|
|
||||||
This module provides a standard socket-like interface for Python
|
|
||||||
for tunneling connections through SOCKS proxies.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
|
|
||||||
for use in PyLoris (http://pyloris.sourceforge.net/)
|
|
||||||
|
|
||||||
Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
|
|
||||||
mainly to merge bug fixes found in Sourceforge
|
|
||||||
|
|
||||||
Minor modifications made by Eugene Dementiev (http://www.dementiev.eu/)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import sys
|
|
||||||
|
|
||||||
PROXY_TYPE_SOCKS4 = 1
|
|
||||||
PROXY_TYPE_SOCKS5 = 2
|
|
||||||
PROXY_TYPE_HTTP = 3
|
|
||||||
|
|
||||||
_defaultproxy = None
|
|
||||||
_orgsocket = socket.socket
|
|
||||||
|
|
||||||
class ProxyError(Exception): pass
|
|
||||||
class GeneralProxyError(ProxyError): pass
|
|
||||||
class Socks5AuthError(ProxyError): pass
|
|
||||||
class Socks5Error(ProxyError): pass
|
|
||||||
class Socks4Error(ProxyError): pass
|
|
||||||
class HTTPError(ProxyError): pass
|
|
||||||
|
|
||||||
_generalerrors = ("success",
|
|
||||||
"invalid data",
|
|
||||||
"not connected",
|
|
||||||
"not available",
|
|
||||||
"bad proxy type",
|
|
||||||
"bad input")
|
|
||||||
|
|
||||||
_socks5errors = ("succeeded",
|
|
||||||
"general SOCKS server failure",
|
|
||||||
"connection not allowed by ruleset",
|
|
||||||
"Network unreachable",
|
|
||||||
"Host unreachable",
|
|
||||||
"Connection refused",
|
|
||||||
"TTL expired",
|
|
||||||
"Command not supported",
|
|
||||||
"Address type not supported",
|
|
||||||
"Unknown error")
|
|
||||||
|
|
||||||
_socks5autherrors = ("succeeded",
|
|
||||||
"authentication is required",
|
|
||||||
"all offered authentication methods were rejected",
|
|
||||||
"unknown username or invalid password",
|
|
||||||
"unknown error")
|
|
||||||
|
|
||||||
_socks4errors = ("request granted",
|
|
||||||
"request rejected or failed",
|
|
||||||
"request rejected because SOCKS server cannot connect to identd on the client",
|
|
||||||
"request rejected because the client program and identd report different user-ids",
|
|
||||||
"unknown error")
|
|
||||||
|
|
||||||
def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
|
|
||||||
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
|
||||||
Sets a default proxy which all further socksocket objects will use,
|
|
||||||
unless explicitly changed.
|
|
||||||
"""
|
|
||||||
global _defaultproxy
|
|
||||||
_defaultproxy = (proxytype, addr, port, rdns, username, password)
|
|
||||||
|
|
||||||
def wrapmodule(module):
|
|
||||||
"""wrapmodule(module)
|
|
||||||
Attempts to replace a module's socket library with a SOCKS socket. Must set
|
|
||||||
a default proxy using setdefaultproxy(...) first.
|
|
||||||
This will only work on modules that import socket directly into the namespace;
|
|
||||||
most of the Python Standard Library falls into this category.
|
|
||||||
"""
|
|
||||||
if _defaultproxy != None:
|
|
||||||
module.socket.socket = socksocket
|
|
||||||
else:
|
|
||||||
raise GeneralProxyError((4, "no proxy specified"))
|
|
||||||
|
|
||||||
class socksocket(socket.socket):
|
|
||||||
"""socksocket([family[, type[, proto]]]) -> socket object
|
|
||||||
Open a SOCKS enabled socket. The parameters are the same as
|
|
||||||
those of the standard socket init. In order for SOCKS to work,
|
|
||||||
you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
|
|
||||||
_orgsocket.__init__(self, family, type, proto, _sock)
|
|
||||||
if _defaultproxy != None:
|
|
||||||
self.__proxy = _defaultproxy
|
|
||||||
else:
|
|
||||||
self.__proxy = (None, None, None, None, None, None)
|
|
||||||
self.__proxysockname = None
|
|
||||||
self.__proxypeername = None
|
|
||||||
|
|
||||||
def __recvall(self, count):
|
|
||||||
"""__recvall(count) -> data
|
|
||||||
Receive EXACTLY the number of bytes requested from the socket.
|
|
||||||
Blocks until the required number of bytes have been received.
|
|
||||||
"""
|
|
||||||
data = self.recv(count)
|
|
||||||
while len(data) < count:
|
|
||||||
d = self.recv(count-len(data))
|
|
||||||
if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
|
|
||||||
data = data + d
|
|
||||||
return data
|
|
||||||
|
|
||||||
def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
|
|
||||||
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
|
||||||
Sets the proxy to be used.
|
|
||||||
proxytype - The type of the proxy to be used. Three types
|
|
||||||
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
|
|
||||||
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
|
|
||||||
addr - The address of the server (IP or DNS).
|
|
||||||
port - The port of the server. Defaults to 1080 for SOCKS
|
|
||||||
servers and 8080 for HTTP proxy servers.
|
|
||||||
rdns - Should DNS queries be preformed on the remote side
|
|
||||||
(rather than the local side). The default is True.
|
|
||||||
Note: This has no effect with SOCKS4 servers.
|
|
||||||
username - Username to authenticate with to the server.
|
|
||||||
The default is no authentication.
|
|
||||||
password - Password to authenticate with to the server.
|
|
||||||
Only relevant when username is also provided.
|
|
||||||
"""
|
|
||||||
self.__proxy = (proxytype, addr, port, rdns, username, password)
|
|
||||||
|
|
||||||
def __negotiatesocks5(self, destaddr, destport):
|
|
||||||
"""__negotiatesocks5(self,destaddr,destport)
|
|
||||||
Negotiates a connection through a SOCKS5 server.
|
|
||||||
"""
|
|
||||||
# First we'll send the authentication packages we support.
|
|
||||||
if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
|
|
||||||
# The username/password details were supplied to the
|
|
||||||
# setproxy method so we support the USERNAME/PASSWORD
|
|
||||||
# authentication (in addition to the standard none).
|
|
||||||
self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
|
|
||||||
else:
|
|
||||||
# No username/password were entered, therefore we
|
|
||||||
# only support connections with no authentication.
|
|
||||||
self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00))
|
|
||||||
# We'll receive the server's response to determine which
|
|
||||||
# method was selected
|
|
||||||
chosenauth = self.__recvall(2)
|
|
||||||
if chosenauth[0:1] != chr(0x05).encode():
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
# Check the chosen authentication method
|
|
||||||
if chosenauth[1:2] == chr(0x00).encode():
|
|
||||||
# No authentication is required
|
|
||||||
pass
|
|
||||||
elif chosenauth[1:2] == chr(0x02).encode():
|
|
||||||
# Okay, we need to perform a basic username/password
|
|
||||||
# authentication.
|
|
||||||
self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
|
|
||||||
authstat = self.__recvall(2)
|
|
||||||
if authstat[0:1] != chr(0x01).encode():
|
|
||||||
# Bad response
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
if authstat[1:2] != chr(0x00).encode():
|
|
||||||
# Authentication failed
|
|
||||||
self.close()
|
|
||||||
raise Socks5AuthError((3, _socks5autherrors[3]))
|
|
||||||
# Authentication succeeded
|
|
||||||
else:
|
|
||||||
# Reaching here is always bad
|
|
||||||
self.close()
|
|
||||||
if chosenauth[1] == chr(0xFF).encode():
|
|
||||||
raise Socks5AuthError((2, _socks5autherrors[2]))
|
|
||||||
else:
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
# Now we can request the actual connection
|
|
||||||
req = struct.pack('BBB', 0x05, 0x01, 0x00)
|
|
||||||
# If the given destination address is an IP address, we'll
|
|
||||||
# use the IPv4 address request even if remote resolving was specified.
|
|
||||||
try:
|
|
||||||
ipaddr = socket.inet_aton(destaddr)
|
|
||||||
req = req + chr(0x01).encode() + ipaddr
|
|
||||||
except socket.error:
|
|
||||||
# Well it's not an IP number, so it's probably a DNS name.
|
|
||||||
if self.__proxy[3]:
|
|
||||||
# Resolve remotely
|
|
||||||
ipaddr = None
|
|
||||||
if type(destaddr) != type(b''): # python3
|
|
||||||
destaddr_bytes = destaddr.encode(encoding='idna')
|
|
||||||
else:
|
|
||||||
destaddr_bytes = destaddr
|
|
||||||
req = req + chr(0x03).encode() + chr(len(destaddr_bytes)).encode() + destaddr_bytes
|
|
||||||
else:
|
|
||||||
# Resolve locally
|
|
||||||
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
|
|
||||||
req = req + chr(0x01).encode() + ipaddr
|
|
||||||
req = req + struct.pack(">H", destport)
|
|
||||||
self.sendall(req)
|
|
||||||
# Get the response
|
|
||||||
resp = self.__recvall(4)
|
|
||||||
if resp[0:1] != chr(0x05).encode():
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
elif resp[1:2] != chr(0x00).encode():
|
|
||||||
# Connection failed
|
|
||||||
self.close()
|
|
||||||
if ord(resp[1:2])<=8:
|
|
||||||
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
|
|
||||||
else:
|
|
||||||
raise Socks5Error((9, _socks5errors[9]))
|
|
||||||
# Get the bound address/port
|
|
||||||
elif resp[3:4] == chr(0x01).encode():
|
|
||||||
boundaddr = self.__recvall(4)
|
|
||||||
elif resp[3:4] == chr(0x03).encode():
|
|
||||||
resp = resp + self.recv(1)
|
|
||||||
boundaddr = self.__recvall(ord(resp[4:5]))
|
|
||||||
else:
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1,_generalerrors[1]))
|
|
||||||
boundport = struct.unpack(">H", self.__recvall(2))[0]
|
|
||||||
self.__proxysockname = (boundaddr, boundport)
|
|
||||||
if ipaddr != None:
|
|
||||||
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
|
|
||||||
else:
|
|
||||||
self.__proxypeername = (destaddr, destport)
|
|
||||||
|
|
||||||
def getproxysockname(self):
|
|
||||||
"""getsockname() -> address info
|
|
||||||
Returns the bound IP address and port number at the proxy.
|
|
||||||
"""
|
|
||||||
return self.__proxysockname
|
|
||||||
|
|
||||||
def getproxypeername(self):
|
|
||||||
"""getproxypeername() -> address info
|
|
||||||
Returns the IP and port number of the proxy.
|
|
||||||
"""
|
|
||||||
return _orgsocket.getpeername(self)
|
|
||||||
|
|
||||||
def getpeername(self):
|
|
||||||
"""getpeername() -> address info
|
|
||||||
Returns the IP address and port number of the destination
|
|
||||||
machine (note: getproxypeername returns the proxy)
|
|
||||||
"""
|
|
||||||
return self.__proxypeername
|
|
||||||
|
|
||||||
def __negotiatesocks4(self,destaddr,destport):
|
|
||||||
"""__negotiatesocks4(self,destaddr,destport)
|
|
||||||
Negotiates a connection through a SOCKS4 server.
|
|
||||||
"""
|
|
||||||
# Check if the destination address provided is an IP address
|
|
||||||
rmtrslv = False
|
|
||||||
try:
|
|
||||||
ipaddr = socket.inet_aton(destaddr)
|
|
||||||
except socket.error:
|
|
||||||
# It's a DNS name. Check where it should be resolved.
|
|
||||||
if self.__proxy[3]:
|
|
||||||
ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
|
|
||||||
rmtrslv = True
|
|
||||||
else:
|
|
||||||
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
|
|
||||||
# Construct the request packet
|
|
||||||
req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
|
|
||||||
# The username parameter is considered userid for SOCKS4
|
|
||||||
if self.__proxy[4] != None:
|
|
||||||
req = req + self.__proxy[4]
|
|
||||||
req = req + chr(0x00).encode()
|
|
||||||
# DNS name if remote resolving is required
|
|
||||||
# NOTE: This is actually an extension to the SOCKS4 protocol
|
|
||||||
# called SOCKS4A and may not be supported in all cases.
|
|
||||||
if rmtrslv:
|
|
||||||
req = req + destaddr + chr(0x00).encode()
|
|
||||||
self.sendall(req)
|
|
||||||
# Get the response from the server
|
|
||||||
resp = self.__recvall(8)
|
|
||||||
if resp[0:1] != chr(0x00).encode():
|
|
||||||
# Bad data
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1,_generalerrors[1]))
|
|
||||||
if resp[1:2] != chr(0x5A).encode():
|
|
||||||
# Server returned an error
|
|
||||||
self.close()
|
|
||||||
if ord(resp[1:2]) in (91, 92, 93):
|
|
||||||
self.close()
|
|
||||||
raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
|
|
||||||
else:
|
|
||||||
raise Socks4Error((94, _socks4errors[4]))
|
|
||||||
# Get the bound address/port
|
|
||||||
self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
|
|
||||||
if rmtrslv != None:
|
|
||||||
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
|
|
||||||
else:
|
|
||||||
self.__proxypeername = (destaddr, destport)
|
|
||||||
|
|
||||||
def __negotiatehttp(self, destaddr, destport):
|
|
||||||
"""__negotiatehttp(self,destaddr,destport)
|
|
||||||
Negotiates a connection through an HTTP server.
|
|
||||||
"""
|
|
||||||
# If we need to resolve locally, we do this now
|
|
||||||
if not self.__proxy[3]:
|
|
||||||
addr = socket.gethostbyname(destaddr)
|
|
||||||
else:
|
|
||||||
addr = destaddr
|
|
||||||
self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
|
|
||||||
# We read the response until we get the string "\r\n\r\n"
|
|
||||||
resp = self.recv(1)
|
|
||||||
while resp.find("\r\n\r\n".encode()) == -1:
|
|
||||||
recv = self.recv(1)
|
|
||||||
if not recv:
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
resp = resp + recv
|
|
||||||
# We just need the first line to check if the connection
|
|
||||||
# was successful
|
|
||||||
statusline = resp.splitlines()[0].split(" ".encode(), 2)
|
|
||||||
if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
try:
|
|
||||||
statuscode = int(statusline[1])
|
|
||||||
except ValueError:
|
|
||||||
self.close()
|
|
||||||
raise GeneralProxyError((1, _generalerrors[1]))
|
|
||||||
if statuscode != 200:
|
|
||||||
self.close()
|
|
||||||
raise HTTPError((statuscode, statusline[2]))
|
|
||||||
self.__proxysockname = ("0.0.0.0", 0)
|
|
||||||
self.__proxypeername = (addr, destport)
|
|
||||||
|
|
||||||
def connect(self, destpair):
|
|
||||||
"""connect(self, despair)
|
|
||||||
Connects to the specified destination through a proxy.
|
|
||||||
destpar - A tuple of the IP/DNS address and the port number.
|
|
||||||
(identical to socket's connect).
|
|
||||||
To select the proxy server use setproxy().
|
|
||||||
"""
|
|
||||||
# Do a minimal input check first
|
|
||||||
if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int):
|
|
||||||
raise GeneralProxyError((5, _generalerrors[5]))
|
|
||||||
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
|
|
||||||
if self.__proxy[2] != None:
|
|
||||||
portnum = int(self.__proxy[2])
|
|
||||||
else:
|
|
||||||
portnum = 1080
|
|
||||||
_orgsocket.connect(self, (self.__proxy[1], portnum))
|
|
||||||
self.__negotiatesocks5(destpair[0], destpair[1])
|
|
||||||
elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
|
|
||||||
if self.__proxy[2] != None:
|
|
||||||
portnum = self.__proxy[2]
|
|
||||||
else:
|
|
||||||
portnum = 1080
|
|
||||||
_orgsocket.connect(self,(self.__proxy[1], portnum))
|
|
||||||
self.__negotiatesocks4(destpair[0], destpair[1])
|
|
||||||
elif self.__proxy[0] == PROXY_TYPE_HTTP:
|
|
||||||
if self.__proxy[2] != None:
|
|
||||||
portnum = self.__proxy[2]
|
|
||||||
else:
|
|
||||||
portnum = 8080
|
|
||||||
_orgsocket.connect(self,(self.__proxy[1], portnum))
|
|
||||||
self.__negotiatehttp(destpair[0], destpair[1])
|
|
||||||
elif self.__proxy[0] == None:
|
|
||||||
_orgsocket.connect(self, (destpair[0], destpair[1]))
|
|
||||||
else:
|
|
||||||
raise GeneralProxyError((4, _generalerrors[4]))
|
|
@ -1,914 +0,0 @@
|
|||||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import contextlib
|
|
||||||
import inspect
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import select
|
|
||||||
import shutil
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
import unittest
|
|
||||||
from ctypes import *
|
|
||||||
from random import Random
|
|
||||||
import functools
|
|
||||||
|
|
||||||
random = Random()
|
|
||||||
|
|
||||||
try:
|
|
||||||
import coloredlogs
|
|
||||||
if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ:
|
|
||||||
os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red'
|
|
||||||
# https://pypi.org/project/coloredlogs/
|
|
||||||
except ImportError as e:
|
|
||||||
coloredlogs = False
|
|
||||||
try:
|
|
||||||
import stem
|
|
||||||
except ImportError as e:
|
|
||||||
stem = False
|
|
||||||
try:
|
|
||||||
import nmap
|
|
||||||
except ImportError as e:
|
|
||||||
nmap = False
|
|
||||||
|
|
||||||
import wrapper
|
|
||||||
from wrapper.toxcore_enums_and_consts import TOX_CONNECTION, TOX_USER_STATUS
|
|
||||||
|
|
||||||
from wrapper_tests.support_http import bAreWeConnected
|
|
||||||
from wrapper_tests.support_onions import (is_valid_fingerprint,
|
|
||||||
lIntroductionPoints,
|
|
||||||
oGetStemController,
|
|
||||||
sMapaddressResolv, sTorResolve)
|
|
||||||
|
|
||||||
try:
|
|
||||||
from user_data.settings import get_user_config_path
|
|
||||||
except ImportError:
|
|
||||||
get_user_config_path = None
|
|
||||||
|
|
||||||
# LOG=util.log
|
|
||||||
global LOG
|
|
||||||
LOG = logging.getLogger()
|
|
||||||
|
|
||||||
def LOG_ERROR(l): print('ERRORc: '+l)
|
|
||||||
def LOG_WARN(l): print('WARNc: ' +l)
|
|
||||||
def LOG_INFO(l): print('INFOc: ' +l)
|
|
||||||
def LOG_DEBUG(l): print('DEBUGc: '+l)
|
|
||||||
def LOG_TRACE(l): pass # print('TRACE+ '+l)
|
|
||||||
|
|
||||||
try:
|
|
||||||
from trepan.api import debug
|
|
||||||
from trepan.interfaces import server as Mserver
|
|
||||||
except:
|
|
||||||
# print('trepan3 TCP server NOT available.')
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# print('trepan3 TCP server available.')
|
|
||||||
def trepan_handler(num=None, f=None):
|
|
||||||
connection_opts={'IO': 'TCP', 'PORT': 6666}
|
|
||||||
intf = Mserver.ServerInterface(connection_opts=connection_opts)
|
|
||||||
dbg_opts = { 'interface': intf }
|
|
||||||
print(f'Starting TCP server listening on port 6666.')
|
|
||||||
debug(dbg_opts=dbg_opts)
|
|
||||||
return
|
|
||||||
|
|
||||||
# self._audio_thread.isAlive
|
|
||||||
iTHREAD_TIMEOUT = 1
|
|
||||||
iTHREAD_SLEEP = 1
|
|
||||||
iTHREAD_JOINS = 8
|
|
||||||
iNODES = 6
|
|
||||||
|
|
||||||
lToxSamplerates = [8000, 12000, 16000, 24000, 48000]
|
|
||||||
lToxSampleratesK = [8, 12, 16, 24, 48]
|
|
||||||
lBOOLEANS = [
|
|
||||||
'local_discovery_enabled',
|
|
||||||
'udp_enabled',
|
|
||||||
'ipv6_enabled',
|
|
||||||
'trace_enabled',
|
|
||||||
'compact_mode',
|
|
||||||
'allow_inline',
|
|
||||||
'notifications',
|
|
||||||
'sound_notifications',
|
|
||||||
'calls_sound',
|
|
||||||
'hole_punching_enabled',
|
|
||||||
'dht_announcements_enabled',
|
|
||||||
'save_history',
|
|
||||||
'download_nodes_list'
|
|
||||||
'core_logging',
|
|
||||||
]
|
|
||||||
|
|
||||||
sDIR = os.environ.get('TMPDIR', '/tmp')
|
|
||||||
sTOX_VERSION = "1000002018"
|
|
||||||
bHAVE_NMAP = shutil.which('nmap')
|
|
||||||
bHAVE_JQ = shutil.which('jq')
|
|
||||||
bHAVE_BASH = shutil.which('bash')
|
|
||||||
bHAVE_TORR = shutil.which('tor-resolve')
|
|
||||||
|
|
||||||
lDEAD_BS = [
|
|
||||||
# Failed to resolve "tox3.plastiras.org"
|
|
||||||
"tox3.plastiras.org",
|
|
||||||
'tox.kolka.tech',
|
|
||||||
# IPs that do not reverse resolve
|
|
||||||
'49.12.229.145',
|
|
||||||
"46.101.197.175",
|
|
||||||
'114.35.245.150',
|
|
||||||
'172.93.52.70',
|
|
||||||
'195.123.208.139',
|
|
||||||
'205.185.115.131',
|
|
||||||
# IPs that do not rreverse resolve
|
|
||||||
'yggnode.cf', '188.225.9.167',
|
|
||||||
'85-143-221-42.simplecloud.ru', '85.143.221.42',
|
|
||||||
# IPs that do not ping
|
|
||||||
'104.244.74.69', 'tox.plastiras.org',
|
|
||||||
'195.123.208.139',
|
|
||||||
'gt.sot-te.ch', '32.226.5.82',
|
|
||||||
# suspicious IPs
|
|
||||||
'tox.abilinski.com', '172.103.164.250', '172.103.164.250.tpia.cipherkey.com',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def assert_main_thread():
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
|
||||||
from qtpy.QtWidgets import QApplication
|
|
||||||
|
|
||||||
# this "instance" method is very useful!
|
|
||||||
app_thread = QtWidgets.QApplication.instance().thread()
|
|
||||||
curr_thread = QtCore.QThread.currentThread()
|
|
||||||
if app_thread != curr_thread:
|
|
||||||
raise RuntimeError('attempt to call MainWindow.append_message from non-app thread')
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def ignoreStdout():
|
|
||||||
devnull = os.open(os.devnull, os.O_WRONLY)
|
|
||||||
old_stdout = os.dup(1)
|
|
||||||
sys.stdout.flush()
|
|
||||||
os.dup2(devnull, 1)
|
|
||||||
os.close(devnull)
|
|
||||||
try:
|
|
||||||
yield
|
|
||||||
finally:
|
|
||||||
os.dup2(old_stdout, 1)
|
|
||||||
os.close(old_stdout)
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def ignoreStderr():
|
|
||||||
devnull = os.open(os.devnull, os.O_WRONLY)
|
|
||||||
old_stderr = os.dup(2)
|
|
||||||
sys.stderr.flush()
|
|
||||||
os.dup2(devnull, 2)
|
|
||||||
os.close(devnull)
|
|
||||||
try:
|
|
||||||
yield
|
|
||||||
finally:
|
|
||||||
os.dup2(old_stderr, 2)
|
|
||||||
os.close(old_stderr)
|
|
||||||
|
|
||||||
def clean_booleans(oArgs):
|
|
||||||
for key in lBOOLEANS:
|
|
||||||
if not hasattr(oArgs, key): continue
|
|
||||||
val = getattr(oArgs, key)
|
|
||||||
if type(val) == bool: continue
|
|
||||||
if val in ['False', 'false', '0']:
|
|
||||||
setattr(oArgs, key, False)
|
|
||||||
else:
|
|
||||||
setattr(oArgs, key, True)
|
|
||||||
|
|
||||||
def on_log(iTox, level, filename, line, func, message, *data):
|
|
||||||
# LOG.debug(repr((level, filename, line, func, message,)))
|
|
||||||
tox_log_cb(level, filename, line, func, message)
|
|
||||||
|
|
||||||
def tox_log_cb(level, filename, line, func, message, *args):
|
|
||||||
"""
|
|
||||||
* @param level The severity of the log message.
|
|
||||||
* @param filename The source file from which the message originated.
|
|
||||||
* @param line The source line from which the message originated.
|
|
||||||
* @param func The function from which the message originated.
|
|
||||||
* @param message The log message.
|
|
||||||
* @param user_data The user data pointer passed to tox_new in options.
|
|
||||||
"""
|
|
||||||
if type(func) == bytes:
|
|
||||||
func = str(func, 'utf-8')
|
|
||||||
message = str(message, 'UTF-8')
|
|
||||||
filename = str(filename, 'UTF-8')
|
|
||||||
|
|
||||||
if filename == 'network.c':
|
|
||||||
if line == 660: return
|
|
||||||
# root WARNING 3network.c#944:b'send_packet'attempted to send message with network family 10 (probably IPv6) on IPv4 socket
|
|
||||||
if line == 944: return
|
|
||||||
i = message.find('07 = GET_NODES')
|
|
||||||
if i > 0:
|
|
||||||
return
|
|
||||||
if filename == 'TCP_common.c': return
|
|
||||||
|
|
||||||
i = message.find(' | ')
|
|
||||||
if i > 0:
|
|
||||||
message = message[:i]
|
|
||||||
# message = filename +'#' +str(line) +':'+func +' '+message
|
|
||||||
|
|
||||||
name = 'core'
|
|
||||||
# old level is meaningless
|
|
||||||
level = 10 # LOG.level
|
|
||||||
|
|
||||||
# LOG._log(LOG.level, f"{level}: {message}", list())
|
|
||||||
|
|
||||||
i = message.find('(0: OK)')
|
|
||||||
if i > 0:
|
|
||||||
level = 10 # LOG.debug
|
|
||||||
else:
|
|
||||||
i = message.find('(1: ')
|
|
||||||
if i > 0:
|
|
||||||
level = 30 # LOG.warn
|
|
||||||
else:
|
|
||||||
level = 20 # LOG.info
|
|
||||||
|
|
||||||
o = LOG.makeRecord(filename, level, func, line, message, list(), None)
|
|
||||||
# LOG.handle(o)
|
|
||||||
LOG_TRACE(f"{level}: {func}{line} {message}")
|
|
||||||
return
|
|
||||||
|
|
||||||
elif level == 1:
|
|
||||||
LOG.critical(f"{level}: {message}")
|
|
||||||
elif level == 2:
|
|
||||||
LOG.error(f"{level}: {message}")
|
|
||||||
elif level == 3:
|
|
||||||
LOG.warn(f"{level}: {message}")
|
|
||||||
elif level == 4:
|
|
||||||
LOG.info(f"{level}: {message}")
|
|
||||||
elif level == 5:
|
|
||||||
LOG.debug(f"{level}: {message}")
|
|
||||||
else:
|
|
||||||
LOG_TRACE(f"{level}: {message}")
|
|
||||||
|
|
||||||
def vAddLoggerCallback(tox_options, callback=None):
|
|
||||||
if callback is None:
|
|
||||||
wrapper.tox.Tox.libtoxcore.tox_options_set_log_callback(
|
|
||||||
tox_options._options_pointer,
|
|
||||||
POINTER(None)())
|
|
||||||
tox_options.self_logger_cb = None
|
|
||||||
return
|
|
||||||
|
|
||||||
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(callback)
|
|
||||||
wrapper.tox.Tox.libtoxcore.tox_options_set_log_callback(
|
|
||||||
tox_options._options_pointer,
|
|
||||||
tox_options.self_logger_cb)
|
|
||||||
|
|
||||||
def get_video_indexes():
|
|
||||||
# Linux
|
|
||||||
return [str(l[5:]) for l in os.listdir('/dev/') if l.startswith('video')]
|
|
||||||
|
|
||||||
def get_audio():
|
|
||||||
with ignoreStderr():
|
|
||||||
import pyaudio
|
|
||||||
oPyA = pyaudio.PyAudio()
|
|
||||||
|
|
||||||
input_devices = output_devices = 0
|
|
||||||
for i in range(oPyA.get_device_count()):
|
|
||||||
device = oPyA.get_device_info_by_index(i)
|
|
||||||
if device["maxInputChannels"]:
|
|
||||||
input_devices += 1
|
|
||||||
if device["maxOutputChannels"]:
|
|
||||||
output_devices += 1
|
|
||||||
# {'index': 21, 'structVersion': 2, 'name': 'default', 'hostApi': 0, 'maxInputChannels': 64, 'maxOutputChannels': 64, 'defaultLowInputLatency': 0.008707482993197279, 'defaultLowOutputLatency': 0.008707482993197279, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
|
|
||||||
audio = {'input': oPyA.get_default_input_device_info()['index'] if input_devices else -1,
|
|
||||||
'output': oPyA.get_default_output_device_info()['index'] if output_devices else -1,
|
|
||||||
'enabled': input_devices and output_devices}
|
|
||||||
return audio
|
|
||||||
|
|
||||||
def oMainArgparser(_=None, iMode=0):
|
|
||||||
# 'Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0'
|
|
||||||
if not os.path.exists('/proc/sys/net/ipv6'):
|
|
||||||
bIpV6 = 'False'
|
|
||||||
else:
|
|
||||||
bIpV6 = 'True'
|
|
||||||
lIpV6Choices=[bIpV6, 'False']
|
|
||||||
|
|
||||||
sNodesJson = os.path.join(os.environ['HOME'], '.config', 'tox', 'DHTnodes.json')
|
|
||||||
if not os.path.exists(sNodesJson): sNodesJson = ''
|
|
||||||
|
|
||||||
logfile = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'toxygen.log')
|
|
||||||
if not os.path.exists(sNodesJson): logfile = ''
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(add_help=True)
|
|
||||||
parser.add_argument('--proxy_host', '--proxy-host', type=str,
|
|
||||||
# oddball - we want to use '' as a setting
|
|
||||||
default='0.0.0.0',
|
|
||||||
help='proxy host')
|
|
||||||
parser.add_argument('--proxy_port', '--proxy-port', default=0, type=int,
|
|
||||||
help='proxy port')
|
|
||||||
parser.add_argument('--proxy_type', '--proxy-type', default=0, type=int,
|
|
||||||
choices=[0,1,2],
|
|
||||||
help='proxy type 1=http, 2=socks')
|
|
||||||
parser.add_argument('--tcp_port', '--tcp-port', default=0, type=int,
|
|
||||||
help='tcp port')
|
|
||||||
parser.add_argument('--udp_enabled', type=str, default='True',
|
|
||||||
choices=['True', 'False'],
|
|
||||||
help='En/Disable udp')
|
|
||||||
parser.add_argument('--ipv6_enabled', type=str, default=bIpV6,
|
|
||||||
choices=lIpV6Choices,
|
|
||||||
help=f"En/Disable ipv6 - default {bIpV6}")
|
|
||||||
parser.add_argument('--trace_enabled',type=str,
|
|
||||||
default='True' if os.environ.get('DEBUG') else 'False',
|
|
||||||
choices=['True','False'],
|
|
||||||
help='Debugging from toxcore logger_trace or env DEBUG=1')
|
|
||||||
parser.add_argument('--download_nodes_list', type=str, default='False',
|
|
||||||
choices=['True', 'False'],
|
|
||||||
help='Download nodes list')
|
|
||||||
parser.add_argument('--nodes_json', type=str,
|
|
||||||
default=sNodesJson)
|
|
||||||
parser.add_argument('--network', type=str,
|
|
||||||
choices=['main', 'local'],
|
|
||||||
default='main')
|
|
||||||
parser.add_argument('--download_nodes_url', type=str,
|
|
||||||
default='https://nodes.tox.chat/json')
|
|
||||||
parser.add_argument('--logfile', default=logfile,
|
|
||||||
help='Filename for logging - start with + for stdout too')
|
|
||||||
parser.add_argument('--loglevel', default=logging.INFO, type=int,
|
|
||||||
# choices=[logging.info,logging.trace,logging.debug,logging.error]
|
|
||||||
help='Threshold for logging (lower is more) default: 20')
|
|
||||||
parser.add_argument('--mode', type=int, default=iMode,
|
|
||||||
choices=[0,1,2],
|
|
||||||
help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0')
|
|
||||||
parser.add_argument('--hole_punching_enabled',type=str,
|
|
||||||
default='False', choices=['True','False'],
|
|
||||||
help='En/Enable hole punching')
|
|
||||||
parser.add_argument('--dht_announcements_enabled',type=str,
|
|
||||||
default='True', choices=['True','False'],
|
|
||||||
help='En/Disable DHT announcements')
|
|
||||||
return parser
|
|
||||||
|
|
||||||
def vSetupLogging(oArgs):
|
|
||||||
global LOG
|
|
||||||
logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S')
|
|
||||||
logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S'
|
|
||||||
logging._defaultFormatter.default_msec_format = ''
|
|
||||||
|
|
||||||
add = None
|
|
||||||
kwargs = dict(level=oArgs.loglevel,
|
|
||||||
format='%(levelname)-8s %(message)s')
|
|
||||||
if oArgs.logfile:
|
|
||||||
add = oArgs.logfile.startswith('+')
|
|
||||||
sub = oArgs.logfile.startswith('-')
|
|
||||||
if add or sub:
|
|
||||||
oArgs.logfile = oArgs.logfile[1:]
|
|
||||||
kwargs['filename'] = oArgs.logfile
|
|
||||||
|
|
||||||
if coloredlogs:
|
|
||||||
# https://pypi.org/project/coloredlogs/
|
|
||||||
aKw = dict(level=oArgs.loglevel,
|
|
||||||
logger=LOG,
|
|
||||||
stream=sys.stdout,
|
|
||||||
fmt='%(name)s %(levelname)s %(message)s'
|
|
||||||
)
|
|
||||||
coloredlogs.install(**aKw)
|
|
||||||
if oArgs.logfile:
|
|
||||||
oHandler = logging.FileHandler(oArgs.logfile)
|
|
||||||
LOG.addHandler(oHandler)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(**kwargs)
|
|
||||||
if add:
|
|
||||||
oHandler = logging.StreamHandler(sys.stdout)
|
|
||||||
LOG.addHandler(oHandler)
|
|
||||||
|
|
||||||
LOG.info(f"Setting loglevel to {oArgs.loglevel!s}")
|
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(oArgs):
|
|
||||||
global LOG
|
|
||||||
if coloredlogs:
|
|
||||||
aKw = dict(level=oArgs.loglevel,
|
|
||||||
logger=LOG,
|
|
||||||
fmt='%(name)s %(levelname)s %(message)s')
|
|
||||||
if oArgs.logfile:
|
|
||||||
oFd = open(oArgs.logfile, 'wt')
|
|
||||||
setattr(oArgs, 'log_oFd', oFd)
|
|
||||||
aKw['stream'] = oFd
|
|
||||||
coloredlogs.install(**aKw)
|
|
||||||
if oArgs.logfile:
|
|
||||||
oHandler = logging.StreamHandler(stream=sys.stdout)
|
|
||||||
LOG.addHandler(oHandler)
|
|
||||||
else:
|
|
||||||
aKw = dict(level=oArgs.loglevel,
|
|
||||||
format='%(name)s %(levelname)-4s %(message)s')
|
|
||||||
if oArgs.logfile:
|
|
||||||
aKw['filename'] = oArgs.logfile
|
|
||||||
logging.basicConfig(**aKw)
|
|
||||||
|
|
||||||
logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S')
|
|
||||||
logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S'
|
|
||||||
logging._defaultFormatter.default_msec_format = ''
|
|
||||||
|
|
||||||
LOG.setLevel(oArgs.loglevel)
|
|
||||||
# LOG.trace = lambda l: LOG.log(0, repr(l))
|
|
||||||
LOG.info(f"Setting loglevel to {oArgs.loglevel!s}")
|
|
||||||
|
|
||||||
def signal_handler(num, f):
|
|
||||||
from trepan.api import debug
|
|
||||||
from trepan.interfaces import server as Mserver
|
|
||||||
connection_opts={'IO': 'TCP', 'PORT': 6666}
|
|
||||||
intf = Mserver.ServerInterface(connection_opts=connection_opts)
|
|
||||||
dbg_opts = {'interface': intf}
|
|
||||||
LOG.info('Starting TCP server listening on port 6666.')
|
|
||||||
debug(dbg_opts=dbg_opts)
|
|
||||||
return
|
|
||||||
|
|
||||||
def merge_args_into_settings(args, settings):
|
|
||||||
if args:
|
|
||||||
if not hasattr(args, 'audio'):
|
|
||||||
LOG.warn('No audio ' +repr(args))
|
|
||||||
settings['audio'] = getattr(args, 'audio')
|
|
||||||
if not hasattr(args, 'video'):
|
|
||||||
LOG.warn('No video ' +repr(args))
|
|
||||||
settings['video'] = getattr(args, 'video')
|
|
||||||
for key in settings.keys():
|
|
||||||
# proxy_type proxy_port proxy_host
|
|
||||||
not_key = 'not_' +key
|
|
||||||
if hasattr(args, key):
|
|
||||||
val = getattr(args, key)
|
|
||||||
if type(val) == bytes:
|
|
||||||
# proxy_host - ascii?
|
|
||||||
# filenames - ascii?
|
|
||||||
val = str(val, 'UTF-8')
|
|
||||||
settings[key] = val
|
|
||||||
elif hasattr(args, not_key):
|
|
||||||
val = not getattr(args, not_key)
|
|
||||||
settings[key] = val
|
|
||||||
clean_settings(settings)
|
|
||||||
return
|
|
||||||
|
|
||||||
def clean_settings(self):
|
|
||||||
# failsafe to ensure C tox is bytes and Py settings is str
|
|
||||||
|
|
||||||
# overrides
|
|
||||||
self['mirror_mode'] = False
|
|
||||||
# REQUIRED!!
|
|
||||||
if not os.path.exists('/proc/sys/net/ipv6'):
|
|
||||||
LOG.warn('Disabling IPV6 because /proc/sys/net/ipv6 does not exist')
|
|
||||||
self['ipv6_enabled'] = False
|
|
||||||
|
|
||||||
if 'proxy_type' in self and self['proxy_type'] == 0:
|
|
||||||
self['proxy_host'] = ''
|
|
||||||
self['proxy_port'] = 0
|
|
||||||
|
|
||||||
if 'proxy_type' in self and self['proxy_type'] != 0 and \
|
|
||||||
'proxy_host' in self and self['proxy_host'] != '' and \
|
|
||||||
'proxy_port' in self and self['proxy_port'] != 0:
|
|
||||||
if 'udp_enabled' in self and self['udp_enabled']:
|
|
||||||
# We don't currently support UDP over proxy.
|
|
||||||
LOG.info("UDP enabled and proxy set: disabling UDP")
|
|
||||||
self['udp_enabled'] = False
|
|
||||||
if 'local_discovery_enabled' in self and self['local_discovery_enabled']:
|
|
||||||
LOG.info("local_discovery_enabled enabled and proxy set: disabling local_discovery_enabled")
|
|
||||||
self['local_discovery_enabled'] = False
|
|
||||||
if 'dht_announcements_enabled' in self and self['dht_announcements_enabled']:
|
|
||||||
LOG.info("dht_announcements_enabled enabled and proxy set: disabling dht_announcements_enabled")
|
|
||||||
self['dht_announcements_enabled'] = False
|
|
||||||
|
|
||||||
if 'auto_accept_path' in self and \
|
|
||||||
type(self['auto_accept_path']) == bytes:
|
|
||||||
self['auto_accept_path'] = str(self['auto_accept_path'], 'UTF-8')
|
|
||||||
|
|
||||||
LOG.debug("Cleaned settings")
|
|
||||||
|
|
||||||
def lSdSamplerates(iDev):
|
|
||||||
try:
|
|
||||||
import sounddevice as sd
|
|
||||||
except ImportError:
|
|
||||||
return []
|
|
||||||
samplerates = (32000, 44100, 48000, 96000, )
|
|
||||||
device = iDev
|
|
||||||
supported_samplerates = []
|
|
||||||
for fs in samplerates:
|
|
||||||
try:
|
|
||||||
sd.check_output_settings(device=device, samplerate=fs)
|
|
||||||
except Exception as e:
|
|
||||||
# LOG.debug(f"Sample rate not supported {fs}" +' '+str(e))
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
supported_samplerates.append(fs)
|
|
||||||
return supported_samplerates
|
|
||||||
|
|
||||||
def _get_nodes_path(oArgs=None):
|
|
||||||
if oArgs and oArgs.nodes_json and os.path.isfile(oArgs.nodes_json):
|
|
||||||
LOG.debug("_get_nodes_path: " +oArgs.nodes_json)
|
|
||||||
default = oArgs.nodes_json
|
|
||||||
elif get_user_config_path:
|
|
||||||
default = os.path.join(get_user_config_path(), 'toxygen_nodes.json')
|
|
||||||
else:
|
|
||||||
# Windwoes
|
|
||||||
default = os.path.join(os.getenv('HOME'), '.config', 'tox', 'toxygen_nodes.json')
|
|
||||||
LOG.debug("_get_nodes_path: " +default)
|
|
||||||
return default
|
|
||||||
|
|
||||||
DEFAULT_NODES_COUNT = 8
|
|
||||||
|
|
||||||
global aNODES
|
|
||||||
aNODES = {}
|
|
||||||
|
|
||||||
|
|
||||||
# @functools.lru_cache(maxsize=12) TypeError: unhashable type: 'Namespace'
|
|
||||||
def generate_nodes(oArgs=None,
|
|
||||||
nodes_count=DEFAULT_NODES_COUNT,
|
|
||||||
ipv='ipv4',
|
|
||||||
udp_not_tcp=True):
|
|
||||||
global aNODES
|
|
||||||
sKey = ipv
|
|
||||||
sKey += ',0' if udp_not_tcp else ',1'
|
|
||||||
if sKey in aNODES and aNODES[sKey]:
|
|
||||||
return aNODES[sKey]
|
|
||||||
sFile = _get_nodes_path(oArgs=oArgs)
|
|
||||||
assert os.path.exists(sFile), sFile
|
|
||||||
lNodes = generate_nodes_from_file(sFile,
|
|
||||||
nodes_count=nodes_count,
|
|
||||||
ipv=ipv, udp_not_tcp=udp_not_tcp)
|
|
||||||
assert lNodes
|
|
||||||
aNODES[sKey] = lNodes
|
|
||||||
return aNODES[sKey]
|
|
||||||
|
|
||||||
aNODES_CACHE = {}
|
|
||||||
def generate_nodes_from_file(sFile,
|
|
||||||
nodes_count=DEFAULT_NODES_COUNT,
|
|
||||||
ipv='ipv4',
|
|
||||||
udp_not_tcp=True,
|
|
||||||
):
|
|
||||||
"""https://github.com/TokTok/c-toxcore/issues/469
|
|
||||||
I had a conversation with @irungentoo on IRC about whether we really need to call tox_bootstrap() when having UDP disabled and why. The answer is yes, because in addition to TCP relays (tox_add_tcp_relay()), toxcore also needs to know addresses of UDP onion nodes in order to work correctly. The DHT, however, is not used when UDP is disabled. tox_bootstrap() function resolves the address passed to it as argument and calls onion_add_bs_node_path() and DHT_bootstrap() functions. Although calling DHT_bootstrap() is not really necessary as DHT is not used, we still need to resolve the address of the DHT node in order to populate the onion routes with onion_add_bs_node_path() call.
|
|
||||||
"""
|
|
||||||
global aNODES_CACHE
|
|
||||||
|
|
||||||
key = ipv
|
|
||||||
key += ',0' if udp_not_tcp else ',1'
|
|
||||||
if key in aNODES_CACHE:
|
|
||||||
sorted_nodes = aNODES_CACHE[key]
|
|
||||||
else:
|
|
||||||
if not os.path.exists(sFile):
|
|
||||||
LOG.error("generate_nodes_from_file file not found " +sFile)
|
|
||||||
return []
|
|
||||||
try:
|
|
||||||
with open(sFile, 'rt') as fl:
|
|
||||||
json_nodes = json.loads(fl.read())['nodes']
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(f"generate_nodes_from_file error {sFile}\n{e}")
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
LOG.debug("generate_nodes_from_file " +sFile)
|
|
||||||
|
|
||||||
if udp_not_tcp:
|
|
||||||
nodes = [(node[ipv], node['port'], node['public_key'],) for
|
|
||||||
node in json_nodes if node[ipv] != 'NONE' \
|
|
||||||
and node["status_udp"] in [True, "true"]
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
nodes = []
|
|
||||||
elts = [(node[ipv], node['tcp_ports'], node['public_key'],) \
|
|
||||||
for node in json_nodes if node[ipv] != 'NONE' \
|
|
||||||
and node["status_tcp"] in [True, "true"]
|
|
||||||
]
|
|
||||||
for (ipv, ports, public_key,) in elts:
|
|
||||||
for port in ports:
|
|
||||||
nodes += [(ipv, port, public_key)]
|
|
||||||
if not nodes:
|
|
||||||
LOG.warn(f'empty generate_nodes from {sFile} {json_nodes!r}')
|
|
||||||
return []
|
|
||||||
sorted_nodes = nodes
|
|
||||||
aNODES_CACHE[key] = sorted_nodes
|
|
||||||
|
|
||||||
random.shuffle(sorted_nodes)
|
|
||||||
if nodes_count is not None and len(sorted_nodes) > nodes_count:
|
|
||||||
sorted_nodes = sorted_nodes[-nodes_count:]
|
|
||||||
LOG.debug(f"generate_nodes_from_file {sFile} len={len(sorted_nodes)}")
|
|
||||||
return sorted_nodes
|
|
||||||
|
|
||||||
def tox_bootstrapd_port():
|
|
||||||
port = 33446
|
|
||||||
sFile = '/etc/tox-bootstrapd.conf'
|
|
||||||
if os.path.exists(sFile):
|
|
||||||
with open(sFile, 'rt') as oFd:
|
|
||||||
for line in oFd.readlines():
|
|
||||||
if line.startswith('port = '):
|
|
||||||
port = int(line[7:])
|
|
||||||
return port
|
|
||||||
|
|
||||||
def bootstrap_local(elts, lToxes, oArgs=None):
|
|
||||||
if os.path.exists('/run/tox-bootstrapd/tox-bootstrapd.pid'):
|
|
||||||
LOG.debug('/run/tox-bootstrapd/tox-bootstrapd.pid')
|
|
||||||
iRet = True
|
|
||||||
else:
|
|
||||||
iRet = os.system("netstat -nle4|grep -q :33")
|
|
||||||
if iRet > 0:
|
|
||||||
LOG.warn(f'bootstraping local No local DHT running')
|
|
||||||
LOG.info(f'bootstraping local')
|
|
||||||
return bootstrap_udp(elts, lToxes, oArgs)
|
|
||||||
|
|
||||||
def lDNSClean(l):
|
|
||||||
global lDEAD_BS
|
|
||||||
# list(set(l).difference(set(lDEAD_BS)))
|
|
||||||
return [elt for elt in l if elt not in lDEAD_BS]
|
|
||||||
|
|
||||||
def lExitExcluder(oArgs, iPort=9051):
|
|
||||||
"""
|
|
||||||
https://raw.githubusercontent.com/nusenu/noContactInfo_Exit_Excluder/main/exclude_noContactInfo_Exits.py
|
|
||||||
"""
|
|
||||||
if not stem:
|
|
||||||
LOG.warn('please install the stem Python package')
|
|
||||||
return ''
|
|
||||||
LOG.debug('lExcludeExitNodes')
|
|
||||||
|
|
||||||
try:
|
|
||||||
controller = oGetStemController(log_level=10)
|
|
||||||
# generator
|
|
||||||
relays = controller.get_server_descriptors()
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(f'Failed to get relay descriptors {e}')
|
|
||||||
return None
|
|
||||||
|
|
||||||
if controller.is_set('ExcludeExitNodes'):
|
|
||||||
LOG.info('ExcludeExitNodes is in use already.')
|
|
||||||
return None
|
|
||||||
|
|
||||||
exit_excludelist=[]
|
|
||||||
LOG.debug("Excluded exit relays:")
|
|
||||||
for relay in relays:
|
|
||||||
if relay.exit_policy.is_exiting_allowed() and not relay.contact:
|
|
||||||
if is_valid_fingerprint(relay.fingerprint):
|
|
||||||
exit_excludelist.append(relay.fingerprint)
|
|
||||||
LOG.debug("https://metrics.torproject.org/rs.html#details/%s" % relay.fingerprint)
|
|
||||||
else:
|
|
||||||
LOG.warn('Invalid Fingerprint: %s' % relay.fingerprint)
|
|
||||||
|
|
||||||
try:
|
|
||||||
controller.set_conf('ExcludeExitNodes', exit_excludelist)
|
|
||||||
LOG.info('Excluded a total of %s exit relays without ContactInfo from the exit position.' % len(exit_excludelist))
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception('ExcludeExitNodes ' +str(e))
|
|
||||||
return exit_excludelist
|
|
||||||
|
|
||||||
aHOSTS = {}
|
|
||||||
@functools.lru_cache(maxsize=20)
|
|
||||||
def sDNSLookup(host):
|
|
||||||
global aHOSTS
|
|
||||||
ipv = 0
|
|
||||||
if host in lDEAD_BS:
|
|
||||||
# LOG.warn(f"address skipped because in lDEAD_BS {host}")
|
|
||||||
return ''
|
|
||||||
if host in aHOSTS:
|
|
||||||
return aHOSTS[host]
|
|
||||||
|
|
||||||
try:
|
|
||||||
s = host.replace('.','')
|
|
||||||
int(s)
|
|
||||||
ipv = 4
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
s = host.replace(':','')
|
|
||||||
int(s)
|
|
||||||
ipv = 6
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
if ipv > 0:
|
|
||||||
# LOG.debug(f"v={ipv} IP address {host}")
|
|
||||||
return host
|
|
||||||
|
|
||||||
LOG.debug(f"sDNSLookup {host}")
|
|
||||||
ip = ''
|
|
||||||
if host.endswith('.tox') or host.endswith('.onion'):
|
|
||||||
if False and stem:
|
|
||||||
ip = sMapaddressResolv(host)
|
|
||||||
if ip: return ip
|
|
||||||
|
|
||||||
ip = sTorResolve(host)
|
|
||||||
if ip: return ip
|
|
||||||
|
|
||||||
if not bHAVE_TORR:
|
|
||||||
LOG.warn(f"onion address skipped because no tor-resolve {host}")
|
|
||||||
return ''
|
|
||||||
try:
|
|
||||||
sout = f"/tmp/TR{os.getpid()}.log"
|
|
||||||
i = os.system(f"tor-resolve -4 {host} > {sout}")
|
|
||||||
if not i:
|
|
||||||
LOG.warn(f"onion address skipped because tor-resolve on {host}")
|
|
||||||
return ''
|
|
||||||
ip = open(sout, 'rt').read()
|
|
||||||
if ip.endswith('failed.'):
|
|
||||||
LOG.warn(f"onion address skipped because tor-resolve failed on {host}")
|
|
||||||
return ''
|
|
||||||
LOG.debug(f"onion address tor-resolve {ip} on {host}")
|
|
||||||
return ip
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
ip = socket.gethostbyname(host)
|
|
||||||
LOG.debug(f"host={host} gethostbyname IP address {ip}")
|
|
||||||
if ip:
|
|
||||||
aHOSTS[host] = ip
|
|
||||||
return ip
|
|
||||||
# drop through
|
|
||||||
except:
|
|
||||||
# drop through
|
|
||||||
pass
|
|
||||||
|
|
||||||
if ip == '':
|
|
||||||
try:
|
|
||||||
sout = f"/tmp/TR{os.getpid()}.log"
|
|
||||||
i = os.system(f"dig {host} +timeout=15|grep ^{host}|sed -e 's/.* //'> {sout}")
|
|
||||||
if not i:
|
|
||||||
LOG.warn(f"address skipped because dig failed on {host}")
|
|
||||||
return ''
|
|
||||||
ip = open(sout, 'rt').read().strip()
|
|
||||||
LOG.debug(f"address dig {ip} on {host}")
|
|
||||||
aHOSTS[host] = ip
|
|
||||||
return ip
|
|
||||||
except:
|
|
||||||
ip = host
|
|
||||||
LOG.debug(f'sDNSLookup {host} -> {ip}')
|
|
||||||
if ip and ip != host:
|
|
||||||
aHOSTS[host] = ip
|
|
||||||
return ip
|
|
||||||
|
|
||||||
def bootstrap_udp(lelts, lToxes, oArgs=None):
|
|
||||||
lelts = lDNSClean(lelts)
|
|
||||||
socket.setdefaulttimeout(15.0)
|
|
||||||
for oTox in lToxes:
|
|
||||||
random.shuffle(lelts)
|
|
||||||
if hasattr(oTox, 'oArgs'):
|
|
||||||
oArgs = oTox.oArgs
|
|
||||||
if hasattr(oArgs, 'contents') and oArgs.contents.proxy_type != 0:
|
|
||||||
lelts = lelts[:1]
|
|
||||||
|
|
||||||
# LOG.debug(f'bootstrap_udp DHT bootstraping {oTox.name} {len(lelts)}')
|
|
||||||
for largs in lelts:
|
|
||||||
assert len(largs) == 3
|
|
||||||
host, port, key = largs
|
|
||||||
assert host; assert port; assert key
|
|
||||||
if host in lDEAD_BS: continue
|
|
||||||
ip = sDNSLookup(host)
|
|
||||||
if not ip:
|
|
||||||
LOG.warn(f'bootstrap_udp to host={host} port={port} did not resolve ip={ip}')
|
|
||||||
continue
|
|
||||||
|
|
||||||
if type(port) == str:
|
|
||||||
port = int(port)
|
|
||||||
try:
|
|
||||||
assert len(key) == 64, key
|
|
||||||
# NOT ip
|
|
||||||
oRet = oTox.bootstrap(host,
|
|
||||||
port,
|
|
||||||
key)
|
|
||||||
except Exception as e:
|
|
||||||
if oArgs is None or (
|
|
||||||
hasattr(oArgs, 'contents') and oArgs.contents.proxy_type == 0):
|
|
||||||
pass
|
|
||||||
# LOG.error(f'bootstrap_udp failed to host={host} port={port} {e}')
|
|
||||||
continue
|
|
||||||
if not oRet:
|
|
||||||
LOG.warn(f'bootstrap_udp failed to {host} : {oRet}')
|
|
||||||
elif oTox.self_get_connection_status() != TOX_CONNECTION['NONE']:
|
|
||||||
LOG.info(f'bootstrap_udp to {host} connected')
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# LOG.debug(f'bootstrap_udp to {host} not connected')
|
|
||||||
pass
|
|
||||||
|
|
||||||
def bootstrap_tcp(lelts, lToxes, oArgs=None):
|
|
||||||
lelts = lDNSClean(lelts)
|
|
||||||
for oTox in lToxes:
|
|
||||||
if hasattr(oTox, 'oArgs'): oArgs = oTox.oArgs
|
|
||||||
random.shuffle(lelts)
|
|
||||||
# LOG.debug(f'bootstrap_tcp bootstapping {oTox.name} {len(lelts)}')
|
|
||||||
for (host, port, key,) in lelts:
|
|
||||||
assert host; assert port;assert key
|
|
||||||
if host in lDEAD_BS: continue
|
|
||||||
ip = sDNSLookup(host)
|
|
||||||
if not ip:
|
|
||||||
LOG.warn(f'bootstrap_tcp to {host} did not resolve ip={ip}')
|
|
||||||
# continue
|
|
||||||
ip = host
|
|
||||||
if host.endswith('.onion') and stem:
|
|
||||||
l = lIntroductionPoints(host)
|
|
||||||
if not l:
|
|
||||||
LOG.warn(f'bootstrap_tcp to {host} has no introduction points')
|
|
||||||
continue
|
|
||||||
if type(port) == str:
|
|
||||||
port = int(port)
|
|
||||||
try:
|
|
||||||
assert len(key) == 64, key
|
|
||||||
oRet = oTox.add_tcp_relay(ip,
|
|
||||||
port,
|
|
||||||
key)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(f'bootstrap_tcp to {host} : ' +str(e))
|
|
||||||
continue
|
|
||||||
if not oRet:
|
|
||||||
LOG.warn(f'bootstrap_tcp failed to {host} : {oRet}')
|
|
||||||
elif oTox.mycon_time == 1:
|
|
||||||
LOG.info(f'bootstrap_tcp to {host} not yet connected last=1')
|
|
||||||
elif oTox.mycon_status is False:
|
|
||||||
LOG.info(f'bootstrap_tcp to {host} not True' \
|
|
||||||
+f" last={int(oTox.mycon_time)}" )
|
|
||||||
elif oTox.self_get_connection_status() != TOX_CONNECTION['NONE']:
|
|
||||||
LOG.info(f'bootstrap_tcp to {host} connected' \
|
|
||||||
+f" last={int(oTox.mycon_time)}" )
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
LOG.debug(f'bootstrap_tcp to {host} but not connected' \
|
|
||||||
+f" last={int(oTox.mycon_time)}" )
|
|
||||||
pass
|
|
||||||
|
|
||||||
def iNmapInfoNmap(sProt, sHost, sPort, key=None, environ=None, cmd=''):
|
|
||||||
if sHost in ['-', 'NONE']: return 0
|
|
||||||
if not nmap: return 0
|
|
||||||
nmps = nmap.PortScanner
|
|
||||||
if sProt in ['socks', 'socks5', 'tcp4']:
|
|
||||||
prot = 'tcp'
|
|
||||||
cmd = f" -Pn -n -sT -p T:{sPort}"
|
|
||||||
else:
|
|
||||||
prot = 'udp'
|
|
||||||
cmd = f" -Pn -n -sU -p U:{sPort}"
|
|
||||||
LOG.debug(f"iNmapInfoNmap cmd={cmd}")
|
|
||||||
sys.stdout.flush()
|
|
||||||
o = nmps().scan(hosts=sHost, arguments=cmd)
|
|
||||||
aScan = o['scan']
|
|
||||||
ip = list(aScan.keys())[0]
|
|
||||||
state = aScan[ip][prot][sPort]['state']
|
|
||||||
LOG.info(f"iNmapInfoNmap: to {sHost} {state}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def iNmapInfo(sProt, sHost, sPort, key=None, environ=None, cmd='nmap'):
|
|
||||||
if sHost in ['-', 'NONE']: return 0
|
|
||||||
sFile = os.path.join("/tmp", f"{sHost}.{os.getpid()}.nmap")
|
|
||||||
if sProt in ['socks', 'socks5', 'tcp4']:
|
|
||||||
cmd += f" -Pn -n -sT -p T:{sPort} {sHost} | grep /tcp "
|
|
||||||
else:
|
|
||||||
cmd += f" -Pn -n -sU -p U:{sPort} {sHost} | grep /udp "
|
|
||||||
LOG.debug(f"iNmapInfo cmd={cmd}")
|
|
||||||
sys.stdout.flush()
|
|
||||||
iRet = os.system('sudo ' +cmd +f" >{sFile} 2>&1 ")
|
|
||||||
LOG.debug(f"iNmapInfo cmd={cmd} iRet={iRet}")
|
|
||||||
if iRet != 0:
|
|
||||||
return iRet
|
|
||||||
assert os.path.exists(sFile), sFile
|
|
||||||
with open(sFile, 'rt') as oFd:
|
|
||||||
l = oFd.readlines()
|
|
||||||
assert len(l)
|
|
||||||
l = [line for line in l if line and not line.startswith('WARNING:')]
|
|
||||||
s = '\n'.join([s.strip() for s in l])
|
|
||||||
LOG.info(f"iNmapInfo: to {sHost}\n{s}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def bootstrap_iNmapInfo(lElts, oArgs, protocol="tcp4", bIS_LOCAL=False, iNODES=iNODES, cmd='nmap'):
|
|
||||||
if not bIS_LOCAL and not bAreWeConnected():
|
|
||||||
LOG.warn(f"bootstrap_iNmapInfo not local and NOT CONNECTED")
|
|
||||||
return True
|
|
||||||
if os.environ['USER'] != 'root':
|
|
||||||
LOG.warn(f"bootstrap_iNmapInfo not ROOT")
|
|
||||||
return True
|
|
||||||
|
|
||||||
lRetval = []
|
|
||||||
for elts in lElts[:iNODES]:
|
|
||||||
host, port, key = elts
|
|
||||||
ip = sDNSLookup(host)
|
|
||||||
if not ip:
|
|
||||||
LOG.info('bootstrap_iNmapInfo to {host} did not resolve ip={ip}')
|
|
||||||
continue
|
|
||||||
if type(port) == str:
|
|
||||||
port = int(port)
|
|
||||||
iRet = -1
|
|
||||||
try:
|
|
||||||
if not nmap:
|
|
||||||
iRet = iNmapInfo(protocol, ip, port, key, cmd=cmd)
|
|
||||||
else:
|
|
||||||
iRet = iNmapInfoNmap(protocol, ip, port, key)
|
|
||||||
if iRet != 0:
|
|
||||||
LOG.warn('iNmapInfo to ' +repr(host) +' retval=' +str(iRet))
|
|
||||||
lRetval += [False]
|
|
||||||
else:
|
|
||||||
LOG.debug('iNmapInfo to ' +repr(host) +' retval=' +str(iRet))
|
|
||||||
lRetval += [True]
|
|
||||||
except Exception as e:
|
|
||||||
LOG.exception('iNmapInfo to {host} : ' +str(e)
|
|
||||||
)
|
|
||||||
lRetval += [False]
|
|
||||||
return any(lRetval)
|
|
||||||
|
|
||||||
def caseFactory(cases):
|
|
||||||
"""We want the tests run in order."""
|
|
||||||
if len(cases) > 1:
|
|
||||||
ordered_cases = sorted(cases, key=lambda f: inspect.findsource(f)[1])
|
|
||||||
else:
|
|
||||||
ordered_cases = cases
|
|
||||||
return ordered_cases
|
|
||||||
|
|
||||||
def suiteFactory(*testcases):
|
|
||||||
"""We want the tests run in order."""
|
|
||||||
linen = lambda f: getattr(tc, f).__code__.co_firstlineno
|
|
||||||
lncmp = lambda a, b: linen(a) - linen(b)
|
|
||||||
|
|
||||||
test_suite = unittest.TestSuite()
|
|
||||||
for tc in testcases:
|
|
||||||
test_suite.addTest(unittest.makeSuite(tc, sortUsing=lncmp))
|
|
||||||
return test_suite
|
|
@ -1,936 +0,0 @@
|
|||||||
# Verify that gdb can pretty-print the various PyObject* types
|
|
||||||
#
|
|
||||||
# The code for testing gdb was adapted from similar work in Unladen Swallow's
|
|
||||||
# Lib/test/test_jit_gdb.py
|
|
||||||
|
|
||||||
import locale
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import sysconfig
|
|
||||||
import textwrap
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
# Is this Python configured to support threads?
|
|
||||||
try:
|
|
||||||
import _thread
|
|
||||||
except ImportError:
|
|
||||||
_thread = None
|
|
||||||
|
|
||||||
from test import support
|
|
||||||
from test.support import run_unittest, findfile, python_is_optimized
|
|
||||||
|
|
||||||
def get_gdb_version():
|
|
||||||
try:
|
|
||||||
proc = subprocess.Popen(["gdb", "-nx", "--version"],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
universal_newlines=True)
|
|
||||||
with proc:
|
|
||||||
version = proc.communicate()[0]
|
|
||||||
except OSError:
|
|
||||||
# This is what "no gdb" looks like. There may, however, be other
|
|
||||||
# errors that manifest this way too.
|
|
||||||
raise unittest.SkipTest("Couldn't find gdb on the path")
|
|
||||||
|
|
||||||
# Regex to parse:
|
|
||||||
# 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7
|
|
||||||
# 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9
|
|
||||||
# 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1
|
|
||||||
# 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5
|
|
||||||
match = re.search(r"^GNU gdb.*?\b(\d+)\.(\d+)", version)
|
|
||||||
if match is None:
|
|
||||||
raise Exception("unable to parse GDB version: %r" % version)
|
|
||||||
return (version, int(match.group(1)), int(match.group(2)))
|
|
||||||
|
|
||||||
gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version()
|
|
||||||
if gdb_major_version < 7:
|
|
||||||
raise unittest.SkipTest("gdb versions before 7.0 didn't support python "
|
|
||||||
"embedding. Saw %s.%s:\n%s"
|
|
||||||
% (gdb_major_version, gdb_minor_version,
|
|
||||||
gdb_version))
|
|
||||||
|
|
||||||
if not sysconfig.is_python_build():
|
|
||||||
raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
|
|
||||||
|
|
||||||
# Location of custom hooks file in a repository checkout.
|
|
||||||
checkout_hook_path = os.path.join(os.path.dirname(sys.executable),
|
|
||||||
'python-gdb.py')
|
|
||||||
|
|
||||||
PYTHONHASHSEED = '123'
|
|
||||||
|
|
||||||
def run_gdb(*args, **env_vars):
|
|
||||||
"""Runs gdb in --batch mode with the additional arguments given by *args.
|
|
||||||
|
|
||||||
Returns its (stdout, stderr) decoded from utf-8 using the replace handler.
|
|
||||||
"""
|
|
||||||
if env_vars:
|
|
||||||
env = os.environ.copy()
|
|
||||||
env.update(env_vars)
|
|
||||||
else:
|
|
||||||
env = None
|
|
||||||
# -nx: Do not execute commands from any .gdbinit initialization files
|
|
||||||
# (issue #22188)
|
|
||||||
base_cmd = ('gdb', '--batch', '-nx')
|
|
||||||
if (gdb_major_version, gdb_minor_version) >= (7, 4):
|
|
||||||
base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
|
|
||||||
proc = subprocess.Popen(base_cmd + args,
|
|
||||||
# Redirect stdin to prevent GDB from messing with
|
|
||||||
# the terminal settings
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
env=env)
|
|
||||||
with proc:
|
|
||||||
out, err = proc.communicate()
|
|
||||||
return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
|
|
||||||
|
|
||||||
# Verify that "gdb" was built with the embedded python support enabled:
|
|
||||||
gdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)")
|
|
||||||
if not gdbpy_version:
|
|
||||||
raise unittest.SkipTest("gdb not built with embedded python support")
|
|
||||||
|
|
||||||
# Verify that "gdb" can load our custom hooks, as OS security settings may
|
|
||||||
# disallow this without a customized .gdbinit.
|
|
||||||
_, gdbpy_errors = run_gdb('--args', sys.executable)
|
|
||||||
if "auto-loading has been declined" in gdbpy_errors:
|
|
||||||
msg = "gdb security settings prevent use of custom hooks: "
|
|
||||||
raise unittest.SkipTest(msg + gdbpy_errors.rstrip())
|
|
||||||
|
|
||||||
def gdb_has_frame_select():
|
|
||||||
# Does this build of gdb have gdb.Frame.select ?
|
|
||||||
stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))")
|
|
||||||
m = re.match(r'.*\[(.*)\].*', stdout)
|
|
||||||
if not m:
|
|
||||||
raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test")
|
|
||||||
gdb_frame_dir = m.group(1).split(', ')
|
|
||||||
return "'select'" in gdb_frame_dir
|
|
||||||
|
|
||||||
HAS_PYUP_PYDOWN = gdb_has_frame_select()
|
|
||||||
|
|
||||||
BREAKPOINT_FN='builtin_id'
|
|
||||||
|
|
||||||
@unittest.skipIf(support.PGO, "not useful for PGO")
|
|
||||||
class DebuggerTests(unittest.TestCase):
|
|
||||||
|
|
||||||
"""Test that the debugger can debug Python."""
|
|
||||||
|
|
||||||
def get_stack_trace(self, source=None, script=None,
|
|
||||||
breakpoint=BREAKPOINT_FN,
|
|
||||||
cmds_after_breakpoint=None,
|
|
||||||
import_site=False):
|
|
||||||
'''
|
|
||||||
Run 'python -c SOURCE' under gdb with a breakpoint.
|
|
||||||
|
|
||||||
Support injecting commands after the breakpoint is reached
|
|
||||||
|
|
||||||
Returns the stdout from gdb
|
|
||||||
|
|
||||||
cmds_after_breakpoint: if provided, a list of strings: gdb commands
|
|
||||||
'''
|
|
||||||
# We use "set breakpoint pending yes" to avoid blocking with a:
|
|
||||||
# Function "foo" not defined.
|
|
||||||
# Make breakpoint pending on future shared library load? (y or [n])
|
|
||||||
# error, which typically happens python is dynamically linked (the
|
|
||||||
# breakpoints of interest are to be found in the shared library)
|
|
||||||
# When this happens, we still get:
|
|
||||||
# Function "textiowrapper_write" not defined.
|
|
||||||
# emitted to stderr each time, alas.
|
|
||||||
|
|
||||||
# Initially I had "--eval-command=continue" here, but removed it to
|
|
||||||
# avoid repeated print breakpoints when traversing hierarchical data
|
|
||||||
# structures
|
|
||||||
|
|
||||||
# Generate a list of commands in gdb's language:
|
|
||||||
commands = ['set breakpoint pending yes',
|
|
||||||
'break %s' % breakpoint,
|
|
||||||
|
|
||||||
# The tests assume that the first frame of printed
|
|
||||||
# backtrace will not contain program counter,
|
|
||||||
# that is however not guaranteed by gdb
|
|
||||||
# therefore we need to use 'set print address off' to
|
|
||||||
# make sure the counter is not there. For example:
|
|
||||||
# #0 in PyObject_Print ...
|
|
||||||
# is assumed, but sometimes this can be e.g.
|
|
||||||
# #0 0x00003fffb7dd1798 in PyObject_Print ...
|
|
||||||
'set print address off',
|
|
||||||
|
|
||||||
'run']
|
|
||||||
|
|
||||||
# GDB as of 7.4 onwards can distinguish between the
|
|
||||||
# value of a variable at entry vs current value:
|
|
||||||
# http://sourceware.org/gdb/onlinedocs/gdb/Variables.html
|
|
||||||
# which leads to the selftests failing with errors like this:
|
|
||||||
# AssertionError: 'v@entry=()' != '()'
|
|
||||||
# Disable this:
|
|
||||||
if (gdb_major_version, gdb_minor_version) >= (7, 4):
|
|
||||||
commands += ['set print entry-values no']
|
|
||||||
|
|
||||||
if cmds_after_breakpoint:
|
|
||||||
commands += cmds_after_breakpoint
|
|
||||||
else:
|
|
||||||
commands += ['backtrace']
|
|
||||||
|
|
||||||
# print commands
|
|
||||||
|
|
||||||
# Use "commands" to generate the arguments with which to invoke "gdb":
|
|
||||||
args = ['--eval-command=%s' % cmd for cmd in commands]
|
|
||||||
args += ["--args",
|
|
||||||
sys.executable]
|
|
||||||
args.extend(subprocess._args_from_interpreter_flags())
|
|
||||||
|
|
||||||
if not import_site:
|
|
||||||
# -S suppresses the default 'import site'
|
|
||||||
args += ["-S"]
|
|
||||||
|
|
||||||
if source:
|
|
||||||
args += ["-c", source]
|
|
||||||
elif script:
|
|
||||||
args += [script]
|
|
||||||
|
|
||||||
# print args
|
|
||||||
# print (' '.join(args))
|
|
||||||
|
|
||||||
# Use "args" to invoke gdb, capturing stdout, stderr:
|
|
||||||
out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED)
|
|
||||||
|
|
||||||
errlines = err.splitlines()
|
|
||||||
unexpected_errlines = []
|
|
||||||
|
|
||||||
# Ignore some benign messages on stderr.
|
|
||||||
ignore_patterns = (
|
|
||||||
'Function "%s" not defined.' % breakpoint,
|
|
||||||
'Do you need "set solib-search-path" or '
|
|
||||||
'"set sysroot"?',
|
|
||||||
# BFD: /usr/lib/debug/(...): unable to initialize decompress
|
|
||||||
# status for section .debug_aranges
|
|
||||||
'BFD: ',
|
|
||||||
# ignore all warnings
|
|
||||||
'warning: ',
|
|
||||||
)
|
|
||||||
for line in errlines:
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
if not line.startswith(ignore_patterns):
|
|
||||||
unexpected_errlines.append(line)
|
|
||||||
|
|
||||||
# Ensure no unexpected error messages:
|
|
||||||
self.assertEqual(unexpected_errlines, [])
|
|
||||||
return out
|
|
||||||
|
|
||||||
def get_gdb_repr(self, source,
|
|
||||||
cmds_after_breakpoint=None,
|
|
||||||
import_site=False):
|
|
||||||
# Given an input python source representation of data,
|
|
||||||
# run "python -c'id(DATA)'" under gdb with a breakpoint on
|
|
||||||
# builtin_id and scrape out gdb's representation of the "op"
|
|
||||||
# parameter, and verify that the gdb displays the same string
|
|
||||||
#
|
|
||||||
# Verify that the gdb displays the expected string
|
|
||||||
#
|
|
||||||
# For a nested structure, the first time we hit the breakpoint will
|
|
||||||
# give us the top-level structure
|
|
||||||
|
|
||||||
# NOTE: avoid decoding too much of the traceback as some
|
|
||||||
# undecodable characters may lurk there in optimized mode
|
|
||||||
# (issue #19743).
|
|
||||||
cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"]
|
|
||||||
gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN,
|
|
||||||
cmds_after_breakpoint=cmds_after_breakpoint,
|
|
||||||
import_site=import_site)
|
|
||||||
# gdb can insert additional '\n' and space characters in various places
|
|
||||||
# in its output, depending on the width of the terminal it's connected
|
|
||||||
# to (using its "wrap_here" function)
|
|
||||||
m = re.match(r'.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*Python/bltinmodule.c.*',
|
|
||||||
gdb_output, re.DOTALL)
|
|
||||||
if not m:
|
|
||||||
self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output))
|
|
||||||
return m.group(1), gdb_output
|
|
||||||
|
|
||||||
def assertEndsWith(self, actual, exp_end):
|
|
||||||
'''Ensure that the given "actual" string ends with "exp_end"'''
|
|
||||||
self.assertTrue(actual.endswith(exp_end),
|
|
||||||
msg='%r did not end with %r' % (actual, exp_end))
|
|
||||||
|
|
||||||
def assertMultilineMatches(self, actual, pattern):
|
|
||||||
m = re.match(pattern, actual, re.DOTALL)
|
|
||||||
if not m:
|
|
||||||
self.fail(msg='%r did not match %r' % (actual, pattern))
|
|
||||||
|
|
||||||
def get_sample_script(self):
|
|
||||||
return findfile('gdb_sample.py')
|
|
||||||
|
|
||||||
class PrettyPrintTests(DebuggerTests):
|
|
||||||
def test_getting_backtrace(self):
|
|
||||||
gdb_output = self.get_stack_trace('id(42)')
|
|
||||||
self.assertTrue(BREAKPOINT_FN in gdb_output)
|
|
||||||
|
|
||||||
def assertGdbRepr(self, val, exp_repr=None):
|
|
||||||
# Ensure that gdb's rendering of the value in a debugged process
|
|
||||||
# matches repr(value) in this process:
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')')
|
|
||||||
if not exp_repr:
|
|
||||||
exp_repr = repr(val)
|
|
||||||
self.assertEqual(gdb_repr, exp_repr,
|
|
||||||
('%r did not equal expected %r; full output was:\n%s'
|
|
||||||
% (gdb_repr, exp_repr, gdb_output)))
|
|
||||||
|
|
||||||
def test_int(self):
|
|
||||||
'Verify the pretty-printing of various int values'
|
|
||||||
self.assertGdbRepr(42)
|
|
||||||
self.assertGdbRepr(0)
|
|
||||||
self.assertGdbRepr(-7)
|
|
||||||
self.assertGdbRepr(1000000000000)
|
|
||||||
self.assertGdbRepr(-1000000000000000)
|
|
||||||
|
|
||||||
def test_singletons(self):
|
|
||||||
'Verify the pretty-printing of True, False and None'
|
|
||||||
self.assertGdbRepr(True)
|
|
||||||
self.assertGdbRepr(False)
|
|
||||||
self.assertGdbRepr(None)
|
|
||||||
|
|
||||||
def test_dicts(self):
|
|
||||||
'Verify the pretty-printing of dictionaries'
|
|
||||||
self.assertGdbRepr({})
|
|
||||||
self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}")
|
|
||||||
# Python preserves insertion order since 3.6
|
|
||||||
self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}")
|
|
||||||
|
|
||||||
def test_lists(self):
|
|
||||||
'Verify the pretty-printing of lists'
|
|
||||||
self.assertGdbRepr([])
|
|
||||||
self.assertGdbRepr(list(range(5)))
|
|
||||||
|
|
||||||
def test_bytes(self):
|
|
||||||
'Verify the pretty-printing of bytes'
|
|
||||||
self.assertGdbRepr(b'')
|
|
||||||
self.assertGdbRepr(b'And now for something hopefully the same')
|
|
||||||
self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text')
|
|
||||||
self.assertGdbRepr(b'this is a tab:\t'
|
|
||||||
b' this is a slash-N:\n'
|
|
||||||
b' this is a slash-R:\r'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80')
|
|
||||||
|
|
||||||
self.assertGdbRepr(bytes([b for b in range(255)]))
|
|
||||||
|
|
||||||
def test_strings(self):
|
|
||||||
'Verify the pretty-printing of unicode strings'
|
|
||||||
encoding = locale.getpreferredencoding()
|
|
||||||
def check_repr(text):
|
|
||||||
try:
|
|
||||||
text.encode(encoding)
|
|
||||||
printable = True
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
self.assertGdbRepr(text, ascii(text))
|
|
||||||
else:
|
|
||||||
self.assertGdbRepr(text)
|
|
||||||
|
|
||||||
self.assertGdbRepr('')
|
|
||||||
self.assertGdbRepr('And now for something hopefully the same')
|
|
||||||
self.assertGdbRepr('string with embedded NUL here \0 and then some more text')
|
|
||||||
|
|
||||||
# Test printing a single character:
|
|
||||||
# U+2620 SKULL AND CROSSBONES
|
|
||||||
check_repr('\u2620')
|
|
||||||
|
|
||||||
# Test printing a Japanese unicode string
|
|
||||||
# (I believe this reads "mojibake", using 3 characters from the CJK
|
|
||||||
# Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE)
|
|
||||||
check_repr('\u6587\u5b57\u5316\u3051')
|
|
||||||
|
|
||||||
# Test a character outside the BMP:
|
|
||||||
# U+1D121 MUSICAL SYMBOL C CLEF
|
|
||||||
# This is:
|
|
||||||
# UTF-8: 0xF0 0x9D 0x84 0xA1
|
|
||||||
# UTF-16: 0xD834 0xDD21
|
|
||||||
check_repr(chr(0x1D121))
|
|
||||||
|
|
||||||
def test_tuples(self):
|
|
||||||
'Verify the pretty-printing of tuples'
|
|
||||||
self.assertGdbRepr(tuple(), '()')
|
|
||||||
self.assertGdbRepr((1,), '(1,)')
|
|
||||||
self.assertGdbRepr(('foo', 'bar', 'baz'))
|
|
||||||
|
|
||||||
def test_sets(self):
|
|
||||||
'Verify the pretty-printing of sets'
|
|
||||||
if (gdb_major_version, gdb_minor_version) < (7, 3):
|
|
||||||
self.skipTest("pretty-printing of sets needs gdb 7.3 or later")
|
|
||||||
self.assertGdbRepr(set(), "set()")
|
|
||||||
self.assertGdbRepr(set(['a']), "{'a'}")
|
|
||||||
# PYTHONHASHSEED is need to get the exact frozenset item order
|
|
||||||
if not sys.flags.ignore_environment:
|
|
||||||
self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}")
|
|
||||||
self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}")
|
|
||||||
|
|
||||||
# Ensure that we handle sets containing the "dummy" key value,
|
|
||||||
# which happens on deletion:
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b'])
|
|
||||||
s.remove('a')
|
|
||||||
id(s)''')
|
|
||||||
self.assertEqual(gdb_repr, "{'b'}")
|
|
||||||
|
|
||||||
def test_frozensets(self):
|
|
||||||
'Verify the pretty-printing of frozensets'
|
|
||||||
if (gdb_major_version, gdb_minor_version) < (7, 3):
|
|
||||||
self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later")
|
|
||||||
self.assertGdbRepr(frozenset(), "frozenset()")
|
|
||||||
self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})")
|
|
||||||
# PYTHONHASHSEED is need to get the exact frozenset item order
|
|
||||||
if not sys.flags.ignore_environment:
|
|
||||||
self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})")
|
|
||||||
self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})")
|
|
||||||
|
|
||||||
def test_exceptions(self):
|
|
||||||
# Test a RuntimeError
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('''
|
|
||||||
try:
|
|
||||||
raise RuntimeError("I am an error")
|
|
||||||
except RuntimeError as e:
|
|
||||||
id(e)
|
|
||||||
''')
|
|
||||||
self.assertEqual(gdb_repr,
|
|
||||||
"RuntimeError('I am an error',)")
|
|
||||||
|
|
||||||
|
|
||||||
# Test division by zero:
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('''
|
|
||||||
try:
|
|
||||||
a = 1 / 0
|
|
||||||
except ZeroDivisionError as e:
|
|
||||||
id(e)
|
|
||||||
''')
|
|
||||||
self.assertEqual(gdb_repr,
|
|
||||||
"ZeroDivisionError('division by zero',)")
|
|
||||||
|
|
||||||
def test_modern_class(self):
|
|
||||||
'Verify the pretty-printing of new-style class instances'
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('''
|
|
||||||
class Foo:
|
|
||||||
pass
|
|
||||||
foo = Foo()
|
|
||||||
foo.an_int = 42
|
|
||||||
id(foo)''')
|
|
||||||
m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
|
|
||||||
self.assertTrue(m,
|
|
||||||
msg='Unexpected new-style class rendering %r' % gdb_repr)
|
|
||||||
|
|
||||||
def test_subclassing_list(self):
|
|
||||||
'Verify the pretty-printing of an instance of a list subclass'
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('''
|
|
||||||
class Foo(list):
|
|
||||||
pass
|
|
||||||
foo = Foo()
|
|
||||||
foo += [1, 2, 3]
|
|
||||||
foo.an_int = 42
|
|
||||||
id(foo)''')
|
|
||||||
m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
|
|
||||||
|
|
||||||
self.assertTrue(m,
|
|
||||||
msg='Unexpected new-style class rendering %r' % gdb_repr)
|
|
||||||
|
|
||||||
def test_subclassing_tuple(self):
|
|
||||||
'Verify the pretty-printing of an instance of a tuple subclass'
|
|
||||||
# This should exercise the negative tp_dictoffset code in the
|
|
||||||
# new-style class support
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('''
|
|
||||||
class Foo(tuple):
|
|
||||||
pass
|
|
||||||
foo = Foo((1, 2, 3))
|
|
||||||
foo.an_int = 42
|
|
||||||
id(foo)''')
|
|
||||||
m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
|
|
||||||
|
|
||||||
self.assertTrue(m,
|
|
||||||
msg='Unexpected new-style class rendering %r' % gdb_repr)
|
|
||||||
|
|
||||||
def assertSane(self, source, corruption, exprepr=None):
|
|
||||||
'''Run Python under gdb, corrupting variables in the inferior process
|
|
||||||
immediately before taking a backtrace.
|
|
||||||
|
|
||||||
Verify that the variable's representation is the expected failsafe
|
|
||||||
representation'''
|
|
||||||
if corruption:
|
|
||||||
cmds_after_breakpoint=[corruption, 'backtrace']
|
|
||||||
else:
|
|
||||||
cmds_after_breakpoint=['backtrace']
|
|
||||||
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr(source,
|
|
||||||
cmds_after_breakpoint=cmds_after_breakpoint)
|
|
||||||
if exprepr:
|
|
||||||
if gdb_repr == exprepr:
|
|
||||||
# gdb managed to print the value in spite of the corruption;
|
|
||||||
# this is good (see http://bugs.python.org/issue8330)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Match anything for the type name; 0xDEADBEEF could point to
|
|
||||||
# something arbitrary (see http://bugs.python.org/issue8330)
|
|
||||||
pattern = '<.* at remote 0x-?[0-9a-f]+>'
|
|
||||||
|
|
||||||
m = re.match(pattern, gdb_repr)
|
|
||||||
if not m:
|
|
||||||
self.fail('Unexpected gdb representation: %r\n%s' % \
|
|
||||||
(gdb_repr, gdb_output))
|
|
||||||
|
|
||||||
def test_NULL_ptr(self):
|
|
||||||
'Ensure that a NULL PyObject* is handled gracefully'
|
|
||||||
gdb_repr, gdb_output = (
|
|
||||||
self.get_gdb_repr('id(42)',
|
|
||||||
cmds_after_breakpoint=['set variable v=0',
|
|
||||||
'backtrace'])
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(gdb_repr, '0x0')
|
|
||||||
|
|
||||||
def test_NULL_ob_type(self):
|
|
||||||
'Ensure that a PyObject* with NULL ob_type is handled gracefully'
|
|
||||||
self.assertSane('id(42)',
|
|
||||||
'set v->ob_type=0')
|
|
||||||
|
|
||||||
def test_corrupt_ob_type(self):
|
|
||||||
'Ensure that a PyObject* with a corrupt ob_type is handled gracefully'
|
|
||||||
self.assertSane('id(42)',
|
|
||||||
'set v->ob_type=0xDEADBEEF',
|
|
||||||
exprepr='42')
|
|
||||||
|
|
||||||
def test_corrupt_tp_flags(self):
|
|
||||||
'Ensure that a PyObject* with a type with corrupt tp_flags is handled'
|
|
||||||
self.assertSane('id(42)',
|
|
||||||
'set v->ob_type->tp_flags=0x0',
|
|
||||||
exprepr='42')
|
|
||||||
|
|
||||||
def test_corrupt_tp_name(self):
|
|
||||||
'Ensure that a PyObject* with a type with corrupt tp_name is handled'
|
|
||||||
self.assertSane('id(42)',
|
|
||||||
'set v->ob_type->tp_name=0xDEADBEEF',
|
|
||||||
exprepr='42')
|
|
||||||
|
|
||||||
def test_builtins_help(self):
|
|
||||||
'Ensure that the new-style class _Helper in site.py can be handled'
|
|
||||||
|
|
||||||
if sys.flags.no_site:
|
|
||||||
self.skipTest("need site module, but -S option was used")
|
|
||||||
|
|
||||||
# (this was the issue causing tracebacks in
|
|
||||||
# http://bugs.python.org/issue8032#msg100537 )
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True)
|
|
||||||
|
|
||||||
m = re.match(r'<_Helper at remote 0x-?[0-9a-f]+>', gdb_repr)
|
|
||||||
self.assertTrue(m,
|
|
||||||
msg='Unexpected rendering %r' % gdb_repr)
|
|
||||||
|
|
||||||
def test_selfreferential_list(self):
|
|
||||||
'''Ensure that a reference loop involving a list doesn't lead proxyval
|
|
||||||
into an infinite loop:'''
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)")
|
|
||||||
self.assertEqual(gdb_repr, '[3, 4, 5, [...]]')
|
|
||||||
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)")
|
|
||||||
self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]')
|
|
||||||
|
|
||||||
def test_selfreferential_dict(self):
|
|
||||||
'''Ensure that a reference loop involving a dict doesn't lead proxyval
|
|
||||||
into an infinite loop:'''
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)")
|
|
||||||
|
|
||||||
self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}")
|
|
||||||
|
|
||||||
def test_selfreferential_old_style_instance(self):
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr('''
|
|
||||||
class Foo:
|
|
||||||
pass
|
|
||||||
foo = Foo()
|
|
||||||
foo.an_attr = foo
|
|
||||||
id(foo)''')
|
|
||||||
self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
|
|
||||||
gdb_repr),
|
|
||||||
'Unexpected gdb representation: %r\n%s' % \
|
|
||||||
(gdb_repr, gdb_output))
|
|
||||||
|
|
||||||
def test_selfreferential_new_style_instance(self):
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr('''
|
|
||||||
class Foo(object):
|
|
||||||
pass
|
|
||||||
foo = Foo()
|
|
||||||
foo.an_attr = foo
|
|
||||||
id(foo)''')
|
|
||||||
self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
|
|
||||||
gdb_repr),
|
|
||||||
'Unexpected gdb representation: %r\n%s' % \
|
|
||||||
(gdb_repr, gdb_output))
|
|
||||||
|
|
||||||
gdb_repr, gdb_output = \
|
|
||||||
self.get_gdb_repr('''
|
|
||||||
class Foo(object):
|
|
||||||
pass
|
|
||||||
a = Foo()
|
|
||||||
b = Foo()
|
|
||||||
a.an_attr = b
|
|
||||||
b.an_attr = a
|
|
||||||
id(a)''')
|
|
||||||
self.assertTrue(re.match(r'<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>',
|
|
||||||
gdb_repr),
|
|
||||||
'Unexpected gdb representation: %r\n%s' % \
|
|
||||||
(gdb_repr, gdb_output))
|
|
||||||
|
|
||||||
def test_truncation(self):
|
|
||||||
'Verify that very long output is truncated'
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))')
|
|
||||||
self.assertEqual(gdb_repr,
|
|
||||||
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "
|
|
||||||
"14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, "
|
|
||||||
"27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, "
|
|
||||||
"40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, "
|
|
||||||
"53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, "
|
|
||||||
"66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, "
|
|
||||||
"79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, "
|
|
||||||
"92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, "
|
|
||||||
"104, 105, 106, 107, 108, 109, 110, 111, 112, 113, "
|
|
||||||
"114, 115, 116, 117, 118, 119, 120, 121, 122, 123, "
|
|
||||||
"124, 125, 126, 127, 128, 129, 130, 131, 132, 133, "
|
|
||||||
"134, 135, 136, 137, 138, 139, 140, 141, 142, 143, "
|
|
||||||
"144, 145, 146, 147, 148, 149, 150, 151, 152, 153, "
|
|
||||||
"154, 155, 156, 157, 158, 159, 160, 161, 162, 163, "
|
|
||||||
"164, 165, 166, 167, 168, 169, 170, 171, 172, 173, "
|
|
||||||
"174, 175, 176, 177, 178, 179, 180, 181, 182, 183, "
|
|
||||||
"184, 185, 186, 187, 188, 189, 190, 191, 192, 193, "
|
|
||||||
"194, 195, 196, 197, 198, 199, 200, 201, 202, 203, "
|
|
||||||
"204, 205, 206, 207, 208, 209, 210, 211, 212, 213, "
|
|
||||||
"214, 215, 216, 217, 218, 219, 220, 221, 222, 223, "
|
|
||||||
"224, 225, 226...(truncated)")
|
|
||||||
self.assertEqual(len(gdb_repr),
|
|
||||||
1024 + len('...(truncated)'))
|
|
||||||
|
|
||||||
def test_builtin_method(self):
|
|
||||||
gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)')
|
|
||||||
self.assertTrue(re.match(r'<built-in method readlines of _io.TextIOWrapper object at remote 0x-?[0-9a-f]+>',
|
|
||||||
gdb_repr),
|
|
||||||
'Unexpected gdb representation: %r\n%s' % \
|
|
||||||
(gdb_repr, gdb_output))
|
|
||||||
|
|
||||||
def test_frames(self):
|
|
||||||
gdb_output = self.get_stack_trace('''
|
|
||||||
def foo(a, b, c):
|
|
||||||
pass
|
|
||||||
|
|
||||||
foo(3, 4, 5)
|
|
||||||
id(foo.__code__)''',
|
|
||||||
breakpoint='builtin_id',
|
|
||||||
cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)']
|
|
||||||
)
|
|
||||||
self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*',
|
|
||||||
gdb_output,
|
|
||||||
re.DOTALL),
|
|
||||||
'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
class PyListTests(DebuggerTests):
|
|
||||||
def assertListing(self, expected, actual):
|
|
||||||
self.assertEndsWith(actual, expected)
|
|
||||||
|
|
||||||
def test_basic_command(self):
|
|
||||||
'Verify that the "py-list" command works'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-list'])
|
|
||||||
|
|
||||||
self.assertListing(' 5 \n'
|
|
||||||
' 6 def bar(a, b, c):\n'
|
|
||||||
' 7 baz(a, b, c)\n'
|
|
||||||
' 8 \n'
|
|
||||||
' 9 def baz(*args):\n'
|
|
||||||
' >10 id(42)\n'
|
|
||||||
' 11 \n'
|
|
||||||
' 12 foo(1, 2, 3)\n',
|
|
||||||
bt)
|
|
||||||
|
|
||||||
def test_one_abs_arg(self):
|
|
||||||
'Verify the "py-list" command with one absolute argument'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-list 9'])
|
|
||||||
|
|
||||||
self.assertListing(' 9 def baz(*args):\n'
|
|
||||||
' >10 id(42)\n'
|
|
||||||
' 11 \n'
|
|
||||||
' 12 foo(1, 2, 3)\n',
|
|
||||||
bt)
|
|
||||||
|
|
||||||
def test_two_abs_args(self):
|
|
||||||
'Verify the "py-list" command with two absolute arguments'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-list 1,3'])
|
|
||||||
|
|
||||||
self.assertListing(' 1 # Sample script for use by test_gdb.py\n'
|
|
||||||
' 2 \n'
|
|
||||||
' 3 def foo(a, b, c):\n',
|
|
||||||
bt)
|
|
||||||
|
|
||||||
class StackNavigationTests(DebuggerTests):
|
|
||||||
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_pyup_command(self):
|
|
||||||
'Verify that the "py-up" command works'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-up'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r'''^.*
|
|
||||||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
|
|
||||||
baz\(a, b, c\)
|
|
||||||
$''')
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
|
|
||||||
def test_down_at_bottom(self):
|
|
||||||
'Verify handling of "py-down" at the bottom of the stack'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-down'])
|
|
||||||
self.assertEndsWith(bt,
|
|
||||||
'Unable to find a newer python frame\n')
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
|
|
||||||
def test_up_at_top(self):
|
|
||||||
'Verify handling of "py-up" at the top of the stack'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up'] * 5)
|
|
||||||
self.assertEndsWith(bt,
|
|
||||||
'Unable to find an older python frame\n')
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_up_then_down(self):
|
|
||||||
'Verify "py-up" followed by "py-down"'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r'''^.*
|
|
||||||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
|
|
||||||
baz\(a, b, c\)
|
|
||||||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
|
|
||||||
id\(42\)
|
|
||||||
$''')
|
|
||||||
|
|
||||||
class PyBtTests(DebuggerTests):
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_bt(self):
|
|
||||||
'Verify that the "py-bt" command works'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-bt'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r'''^.*
|
|
||||||
Traceback \(most recent call first\):
|
|
||||||
<built-in method id of module object .*>
|
|
||||||
File ".*gdb_sample.py", line 10, in baz
|
|
||||||
id\(42\)
|
|
||||||
File ".*gdb_sample.py", line 7, in bar
|
|
||||||
baz\(a, b, c\)
|
|
||||||
File ".*gdb_sample.py", line 4, in foo
|
|
||||||
bar\(a, b, c\)
|
|
||||||
File ".*gdb_sample.py", line 12, in <module>
|
|
||||||
foo\(1, 2, 3\)
|
|
||||||
''')
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_bt_full(self):
|
|
||||||
'Verify that the "py-bt-full" command works'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-bt-full'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r'''^.*
|
|
||||||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
|
|
||||||
baz\(a, b, c\)
|
|
||||||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
|
|
||||||
bar\(a, b, c\)
|
|
||||||
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
|
|
||||||
foo\(1, 2, 3\)
|
|
||||||
''')
|
|
||||||
|
|
||||||
@unittest.skipUnless(_thread,
|
|
||||||
"Python was compiled without thread support")
|
|
||||||
def test_threads(self):
|
|
||||||
'Verify that "py-bt" indicates threads that are waiting for the GIL'
|
|
||||||
cmd = '''
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
class TestThread(Thread):
|
|
||||||
# These threads would run forever, but we'll interrupt things with the
|
|
||||||
# debugger
|
|
||||||
def run(self):
|
|
||||||
i = 0
|
|
||||||
while 1:
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
t = {}
|
|
||||||
for i in range(4):
|
|
||||||
t[i] = TestThread()
|
|
||||||
t[i].start()
|
|
||||||
|
|
||||||
# Trigger a breakpoint on the main thread
|
|
||||||
id(42)
|
|
||||||
|
|
||||||
'''
|
|
||||||
# Verify with "py-bt":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
cmds_after_breakpoint=['thread apply all py-bt'])
|
|
||||||
self.assertIn('Waiting for the GIL', gdb_output)
|
|
||||||
|
|
||||||
# Verify with "py-bt-full":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
cmds_after_breakpoint=['thread apply all py-bt-full'])
|
|
||||||
self.assertIn('Waiting for the GIL', gdb_output)
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
# Some older versions of gdb will fail with
|
|
||||||
# "Cannot find new threads: generic error"
|
|
||||||
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
|
|
||||||
@unittest.skipUnless(_thread,
|
|
||||||
"Python was compiled without thread support")
|
|
||||||
def test_gc(self):
|
|
||||||
'Verify that "py-bt" indicates if a thread is garbage-collecting'
|
|
||||||
cmd = ('from gc import collect\n'
|
|
||||||
'id(42)\n'
|
|
||||||
'def foo():\n'
|
|
||||||
' collect()\n'
|
|
||||||
'def bar():\n'
|
|
||||||
' foo()\n'
|
|
||||||
'bar()\n')
|
|
||||||
# Verify with "py-bt":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'],
|
|
||||||
)
|
|
||||||
self.assertIn('Garbage-collecting', gdb_output)
|
|
||||||
|
|
||||||
# Verify with "py-bt-full":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'],
|
|
||||||
)
|
|
||||||
self.assertIn('Garbage-collecting', gdb_output)
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
# Some older versions of gdb will fail with
|
|
||||||
# "Cannot find new threads: generic error"
|
|
||||||
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
|
|
||||||
@unittest.skipUnless(_thread,
|
|
||||||
"Python was compiled without thread support")
|
|
||||||
def test_pycfunction(self):
|
|
||||||
'Verify that "py-bt" displays invocations of PyCFunction instances'
|
|
||||||
# Tested function must not be defined with METH_NOARGS or METH_O,
|
|
||||||
# otherwise call_function() doesn't call PyCFunction_Call()
|
|
||||||
cmd = ('from time import gmtime\n'
|
|
||||||
'def foo():\n'
|
|
||||||
' gmtime(1)\n'
|
|
||||||
'def bar():\n'
|
|
||||||
' foo()\n'
|
|
||||||
'bar()\n')
|
|
||||||
# Verify with "py-bt":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
breakpoint='time_gmtime',
|
|
||||||
cmds_after_breakpoint=['bt', 'py-bt'],
|
|
||||||
)
|
|
||||||
self.assertIn('<built-in method gmtime', gdb_output)
|
|
||||||
|
|
||||||
# Verify with "py-bt-full":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
breakpoint='time_gmtime',
|
|
||||||
cmds_after_breakpoint=['py-bt-full'],
|
|
||||||
)
|
|
||||||
self.assertIn('#2 <built-in method gmtime', gdb_output)
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_wrapper_call(self):
|
|
||||||
cmd = textwrap.dedent('''
|
|
||||||
class MyList(list):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__() # wrapper_call()
|
|
||||||
|
|
||||||
id("first break point")
|
|
||||||
l = MyList()
|
|
||||||
''')
|
|
||||||
# Verify with "py-bt":
|
|
||||||
gdb_output = self.get_stack_trace(cmd,
|
|
||||||
cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
|
|
||||||
self.assertRegex(gdb_output,
|
|
||||||
r"<method-wrapper u?'__init__' of MyList object at ")
|
|
||||||
|
|
||||||
|
|
||||||
class PyPrintTests(DebuggerTests):
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_basic_command(self):
|
|
||||||
'Verify that the "py-print" command works'
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-print args'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r".*\nlocal 'args' = \(1, 2, 3\)\n.*")
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
|
|
||||||
def test_print_after_up(self):
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_printing_global(self):
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-print __name__'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r".*\nglobal '__name__' = '__main__'\n.*")
|
|
||||||
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_printing_builtin(self):
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-print len'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
|
|
||||||
|
|
||||||
class PyLocalsTests(DebuggerTests):
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_basic_command(self):
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-locals'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r".*\nargs = \(1, 2, 3\)\n.*")
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
|
|
||||||
@unittest.skipIf(python_is_optimized(),
|
|
||||||
"Python was compiled with optimizations")
|
|
||||||
def test_locals_after_up(self):
|
|
||||||
bt = self.get_stack_trace(script=self.get_sample_script(),
|
|
||||||
cmds_after_breakpoint=['py-up', 'py-up', 'py-locals'])
|
|
||||||
self.assertMultilineMatches(bt,
|
|
||||||
r".*\na = 1\nb = 2\nc = 3\n.*")
|
|
||||||
|
|
||||||
def test_main():
|
|
||||||
if support.verbose:
|
|
||||||
print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version))
|
|
||||||
for line in gdb_version.splitlines():
|
|
||||||
print(" " * 4 + line)
|
|
||||||
run_unittest(PrettyPrintTests,
|
|
||||||
PyListTests,
|
|
||||||
StackNavigationTests,
|
|
||||||
PyBtTests,
|
|
||||||
PyPrintTests,
|
|
||||||
PyLocalsTests
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_main()
|
|
@ -1 +0,0 @@
|
|||||||
https://github.com/akheron/cpython/raw/master/Lib/test/test_gdb.py
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
|
|||||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
||||||
|
|
||||||
from notifications import sound
|
|
||||||
from notifications.sound import SOUND_NOTIFICATION
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
if True:
|
|
||||||
def test_sound_notification(self):
|
|
||||||
"""
|
|
||||||
Plays sound notification
|
|
||||||
:param type of notification
|
|
||||||
"""
|
|
||||||
sound.sound_notification( SOUND_NOTIFICATION['MESSAGE'] )
|
|
||||||
sleep(10)
|
|
||||||
sound.sound_notification( SOUND_NOTIFICATION['FILE_TRANSFER'] )
|
|
||||||
sleep(10)
|
|
||||||
sound.sound_notification( None )
|
|
4
toxygen/third_party/qweechat/qweechat.py
vendored
4
toxygen/third_party/qweechat/qweechat.py
vendored
@ -90,7 +90,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
|
self.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
|
||||||
QtWidgets.QSizePolicy.Preferred)
|
QtWidgets.QSizePolicy.Preferred)
|
||||||
self.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
self.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
||||||
QtWidgets.QSizePolicy.Expanding)
|
QtWidgets.QSizePolicy.Expanding)
|
||||||
# MainWindow
|
# MainWindow
|
||||||
self.setCentralWidget(splitter)
|
self.setCentralWidget(splitter)
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.actions['preferences'],
|
self.actions['preferences'],
|
||||||
self.actions['about'],
|
self.actions['about'],
|
||||||
self.actions['quit']])
|
self.actions['quit']])
|
||||||
self.toolbar = toolbar
|
self.toolbar = toolbar
|
||||||
self.buffers[0].widget.input.setFocus()
|
self.buffers[0].widget.input.setFocus()
|
||||||
|
|
||||||
# open debug dialog
|
# open debug dialog
|
||||||
|
@ -98,7 +98,9 @@ class MessagesItemsFactory:
|
|||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _create_message_browser(self, text, width, message_type, parent=None):
|
def _create_message_browser(self, text, width, message_type, parent=None):
|
||||||
return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader,
|
return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader,
|
||||||
|
@ -3,7 +3,7 @@ import os
|
|||||||
|
|
||||||
from PyQt5 import uic
|
from PyQt5 import uic
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
from qtpy.QtGui import (QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QFontMetrics)
|
from qtpy.QtGui import (QColor, QTextCharFormat, QFont, QSyntaxHighlighter)
|
||||||
|
|
||||||
from ui.contact_items import *
|
from ui.contact_items import *
|
||||||
from ui.widgets import MultilineEdit
|
from ui.widgets import MultilineEdit
|
||||||
@ -652,7 +652,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
else:
|
else:
|
||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Functions which called when user click in menu
|
# Functions which called when user click in menu
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def log_console(self):
|
def log_console(self):
|
||||||
self._me.show()
|
self._me.show()
|
||||||
@ -757,7 +759,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self._we.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
|
self._we.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
|
||||||
QtWidgets.QSizePolicy.Preferred)
|
QtWidgets.QSizePolicy.Preferred)
|
||||||
self._we.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
self._we.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
||||||
QtWidgets.QSizePolicy.Expanding)
|
QtWidgets.QSizePolicy.Expanding)
|
||||||
|
|
||||||
LOG.info("Showing WeechatConsole")
|
LOG.info("Showing WeechatConsole")
|
||||||
self._we.show()
|
self._we.show()
|
||||||
@ -875,7 +877,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
120))
|
120))
|
||||||
self.menu.show()
|
self.menu.show()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Messages, calls and file transfers
|
# Messages, calls and file transfers
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_message(self):
|
def send_message(self):
|
||||||
self._messenger.send_message()
|
self._messenger.send_message()
|
||||||
@ -938,7 +942,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.videocallButton.setIcon(icon)
|
self.videocallButton.setIcon(icon)
|
||||||
self.videocallButton.setIconSize(QtCore.QSize(35, 35))
|
self.videocallButton.setIconSize(QtCore.QSize(35, 35))
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Functions which called when user open context menu in friends list
|
# Functions which called when user open context menu in friends list
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _friend_right_click(self, pos):
|
def _friend_right_click(self, pos):
|
||||||
item = self.friends_list.itemAt(pos)
|
item = self.friends_list.itemAt(pos)
|
||||||
@ -995,7 +1001,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def select_contact_row(self, row_index):
|
def select_contact_row(self, row_index):
|
||||||
self.friends_list.setCurrentRow(row_index)
|
self.friends_list.setCurrentRow(row_index)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Functions which called when user click somewhere else
|
# Functions which called when user click somewhere else
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _selected_contact_changed(self):
|
def _selected_contact_changed(self):
|
||||||
num = self.friends_list.currentRow()
|
num = self.friends_list.currentRow()
|
||||||
|
@ -48,7 +48,7 @@ class MessageBrowser(QtWidgets.QTextBrowser):
|
|||||||
# resize(self, a0: QSize): argument 1 has unexpected type 'int'
|
# resize(self, a0: QSize): argument 1 has unexpected type 'int'
|
||||||
# resize(self, w: int, h: int): argument 2 has unexpected type 'float'
|
# resize(self, w: int, h: int): argument 2 has unexpected type 'float'
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
|
self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
|
||||||
self.anchorClicked.connect(self.on_anchor_clicked)
|
self.anchorClicked.connect(self.on_anchor_clicked)
|
||||||
|
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>560</width>
|
|
||||||
<height>320</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>560</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>560</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="toxIdLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="messageLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPlainTextEdit" name="messagePlainTextEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>110</y>
|
|
||||||
<width>460</width>
|
|
||||||
<height>150</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="addBootstrapPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>270</y>
|
|
||||||
<width>460</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="errorLabel">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>220</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>321</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="contextMenuPolicy">
|
|
||||||
<enum>Qt::NoContextMenu</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,99 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>560</width>
|
|
||||||
<height>320</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>560</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>560</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="toxIdLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="messageLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPlainTextEdit" name="messagePlainTextEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>110</y>
|
|
||||||
<width>460</width>
|
|
||||||
<height>150</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="addContactPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>270</y>
|
|
||||||
<width>460</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="errorLabel">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>220</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>321</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="contextMenuPolicy">
|
|
||||||
<enum>Qt::NoContextMenu</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>315</width>
|
|
||||||
<height>218</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>315</width>
|
|
||||||
<height>218</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>315</width>
|
|
||||||
<height>218</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="inputDeviceLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>261</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>16</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="outputDeviceLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>261</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>16</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="inputDeviceComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>50</y>
|
|
||||||
<width>255</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="outputDeviceComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>255</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>375</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QListWidget" name="bansListWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>375</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,127 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>640</width>
|
|
||||||
<height>300</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QPushButton" name="addGroupButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>250</y>
|
|
||||||
<width>601</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="groupNameLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>150</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>470</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="groupTypeComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>150</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>470</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="groupNameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>121</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="groupTypeLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>121</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="statusLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>200</y>
|
|
||||||
<width>111</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="nickLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>150</y>
|
|
||||||
<width>111</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="nickLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>150</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>470</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="statusComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>150</x>
|
|
||||||
<y>190</y>
|
|
||||||
<width>470</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,128 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>340</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>340</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>340</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QPushButton" name="createProfile">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>270</y>
|
|
||||||
<width>341</width>
|
|
||||||
<height>51</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="confirmPassword">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>170</y>
|
|
||||||
<width>341</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="echoMode">
|
|
||||||
<enum>QLineEdit::Password</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="password">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>120</y>
|
|
||||||
<width>341</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="echoMode">
|
|
||||||
<enum>QLineEdit::Password</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="passwordLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>330</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="defaultFolder">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>330</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="programFolder">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>330</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="errorLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>220</y>
|
|
||||||
<width>341</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,58 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>100</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QPushButton" name="cancelPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>330</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>161</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="banTargetLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>15</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>305</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="banTimeLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>15</x>
|
|
||||||
<y>50</y>
|
|
||||||
<width>305</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,71 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>150</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="friendNameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>250</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>300</width>
|
|
||||||
<height>21</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="groupNameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>250</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>300</width>
|
|
||||||
<height>21</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="friendAvatarLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>140</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>60</width>
|
|
||||||
<height>60</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="selectCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>50</y>
|
|
||||||
<width>20</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,83 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>220</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>220</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>220</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="passwordLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>380</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="copyPasswordPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>380</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="peerLimitLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>120</y>
|
|
||||||
<width>380</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="privacyStateLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>160</y>
|
|
||||||
<width>380</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,113 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="noInvitesLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>150</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>25</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QListWidget" name="invitesListWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>341</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="nickLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>360</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="passwordLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>410</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="statusComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>390</x>
|
|
||||||
<y>390</y>
|
|
||||||
<width>200</width>
|
|
||||||
<height>35</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="acceptPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>460</y>
|
|
||||||
<width>201</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="declinePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>360</x>
|
|
||||||
<y>460</y>
|
|
||||||
<width>201</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,123 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>658</width>
|
|
||||||
<height>283</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLineEdit" name="passwordLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>180</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>450</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="passwordLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>145</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="peerLimitLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>145</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QSpinBox" name="peersLimitSpinBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>180</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>450</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>9999</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>512</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="privacyStateLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>130</y>
|
|
||||||
<width>145</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="privacyStateComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>180</x>
|
|
||||||
<y>120</y>
|
|
||||||
<width>450</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="deletePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>180</y>
|
|
||||||
<width>300</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="savePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>220</y>
|
|
||||||
<width>611</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,255 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>552</width>
|
|
||||||
<height>847</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QScrollArea" name="scrollArea">
|
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
|
||||||
</property>
|
|
||||||
<property name="widgetResizable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="scrollAreaWidgetContents_3">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>532</width>
|
|
||||||
<height>827</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="themeLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="themeComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>180</y>
|
|
||||||
<width>471</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="languageComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>471</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="languageLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<!--
|
|
||||||
<widget class="QCheckBox" name="mirrorModeCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>220</y>
|
|
||||||
<width>461</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
-->
|
|
||||||
<widget class="QGroupBox" name="smileysGroupBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>280</y>
|
|
||||||
<width>461</width>
|
|
||||||
<height>221</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>GroupBox</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QCheckBox" name="smileysCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>92</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="smileysPackLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>411</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="smileysPackComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>120</y>
|
|
||||||
<width>411</width>
|
|
||||||
<height>31</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="compactModeCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>250</y>
|
|
||||||
<width>461</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="importStickersPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>750</y>
|
|
||||||
<width>471</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="importSmileysPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>690</y>
|
|
||||||
<width>471</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="showAvatarsCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>520</y>
|
|
||||||
<width>461</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QGroupBox" name="appClosingGroupBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>550</y>
|
|
||||||
<width>471</width>
|
|
||||||
<height>131</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>GroupBox</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QRadioButton" name="closeRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="hideRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>431</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="closeToTrayRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>90</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,139 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>740</width>
|
|
||||||
<height>320</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>740</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>740</width>
|
|
||||||
<height>320</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="chatIdLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="passwordLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>90</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="joinGroupButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>260</y>
|
|
||||||
<width>680</width>
|
|
||||||
<height>51</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="chatIdLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>190</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>520</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="passwordLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>190</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>520</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="nickLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>150</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="statusLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>210</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>17</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="nickLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>190</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>520</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="statusComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>190</x>
|
|
||||||
<y>200</y>
|
|
||||||
<width>520</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,135 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>loginScreen</class>
|
|
||||||
<widget class="QWidget" name="loginScreen">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>200</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>200</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>200</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="toxygenLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>5</y>
|
|
||||||
<width>401</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>16</pointsize>
|
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Toxygen</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QGroupBox" name="newProfileGroupBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>180</width>
|
|
||||||
<height>150</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>GroupBox</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
<widget class="QPushButton" name="createProfilePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>110</y>
|
|
||||||
<width>160</width>
|
|
||||||
<height>27</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<widget class="QGroupBox" name="existingProfileGroupBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>210</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>180</width>
|
|
||||||
<height>150</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>GroupBox</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
<widget class="QComboBox" name="profilesComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>160</width>
|
|
||||||
<height>27</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="defaultProfileCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>75</y>
|
|
||||||
<width>160</width>
|
|
||||||
<height>27</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="loadProfilePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>10</x>
|
|
||||||
<y>110</y>
|
|
||||||
<width>160</width>
|
|
||||||
<height>27</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,94 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>270</width>
|
|
||||||
<height>500</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="cursor">
|
|
||||||
<cursorShape>PointingHandCursor</cursorShape>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="avatarLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>5</x>
|
|
||||||
<y>5</y>
|
|
||||||
<width>64</width>
|
|
||||||
<height>64</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="cursor">
|
|
||||||
<cursorShape>PointingHandCursor</cursorShape>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="searchLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>75</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>25</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="contactsFilterComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>150</x>
|
|
||||||
<y>75</y>
|
|
||||||
<width>120</width>
|
|
||||||
<height>25</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="searchLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>77</y>
|
|
||||||
<width>20</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QListWidget" name="friendsListWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>270</width>
|
|
||||||
<height>400</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="groupInvitesPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>270</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,196 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>545</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>545</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>545</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QCheckBox" name="ipv6CheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="udpCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>210</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="proxyCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="httpProxyRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>190</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>25</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="socksProxyRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>230</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>25</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="lanCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>150</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="ipLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>280</y>
|
|
||||||
<width>60</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="portLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>330</y>
|
|
||||||
<width>60</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="urlLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>380</y>
|
|
||||||
<width>60</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Chat Url</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="restartCorePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>430</y>
|
|
||||||
<width>340</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="downloadNodesCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>340</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="warningLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>480</y>
|
|
||||||
<width>340</width>
|
|
||||||
<height>65</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,71 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>320</width>
|
|
||||||
<height>201</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QCheckBox" name="notificationsCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>20</y>
|
|
||||||
<width>271</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="soundNotificationsCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>271</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="groupNotificationsCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>271</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="callsSoundCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>20</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>271</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,202 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="peerNameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>110</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>431</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="sendPrivateMessagePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>140</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>50</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QCheckBox" name="ignorePeerCheckBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>CheckBox</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QGroupBox" name="banGroupBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>300</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>161</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>GroupBox</string>
|
|
||||||
</property>
|
|
||||||
<!--
|
|
||||||
<widget class="QPushButton" name="banPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>380</x>
|
|
||||||
<y>50</y>
|
|
||||||
<width>101</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
-->
|
|
||||||
<widget class="QRadioButton" name="ipBanRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>251</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="nickBanRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>251</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QRadioButton" name="pkBanRadioButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>120</y>
|
|
||||||
<width>251</width>
|
|
||||||
<height>23</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RadioButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="kickPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>380</x>
|
|
||||||
<y>100</y>
|
|
||||||
<width>101</width>
|
|
||||||
<height>41</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="roleLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="roleNameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>130</x>
|
|
||||||
<y>60</y>
|
|
||||||
<width>411</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="copyPublicKeyPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>210</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>50</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="rolesComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>130</x>
|
|
||||||
<y>55</y>
|
|
||||||
<width>291</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,280 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>900</width>
|
|
||||||
<height>680</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="nameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>10</y>
|
|
||||||
<width>161</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="statusLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>90</y>
|
|
||||||
<width>161</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="nameLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>50</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="statusMessageLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>30</x>
|
|
||||||
<y>130</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="statusComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>520</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>311</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="toxIdTitleLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>180</y>
|
|
||||||
<width>131</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="toxIdLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>210</y>
|
|
||||||
<width>831</width>
|
|
||||||
<height>60</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="copyToxIdPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>280</y>
|
|
||||||
<width>371</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="copyPublicKeyPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>440</x>
|
|
||||||
<y>280</y>
|
|
||||||
<width>371</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="newAvatarPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>520</x>
|
|
||||||
<y>80</y>
|
|
||||||
<width>321</width>
|
|
||||||
<height>34</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="resetAvatarPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>520</x>
|
|
||||||
<y>130</y>
|
|
||||||
<width>321</width>
|
|
||||||
<height>34</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="profilePasswordLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>60</x>
|
|
||||||
<y>380</y>
|
|
||||||
<width>161</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="passwordLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>420</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLineEdit" name="confirmPasswordLineEdit">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>470</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="emptyPasswordLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>500</x>
|
|
||||||
<y>420</y>
|
|
||||||
<width>381</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="warningLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>60</x>
|
|
||||||
<y>580</y>
|
|
||||||
<width>381</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="defaultProfilePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>630</y>
|
|
||||||
<width>831</width>
|
|
||||||
<height>34</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="changePasswordPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>520</y>
|
|
||||||
<width>421</width>
|
|
||||||
<height>34</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="invalidPasswordsLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>500</x>
|
|
||||||
<y>470</y>
|
|
||||||
<width>381</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="exportProfilePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>40</x>
|
|
||||||
<y>330</y>
|
|
||||||
<width>371</width>
|
|
||||||
<height>34</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="newNoSpamPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>440</x>
|
|
||||||
<y>330</y>
|
|
||||||
<width>371</width>
|
|
||||||
<height>34</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,119 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>500</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="statusLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>120</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="copyPublicKeyPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>250</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>50</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="statusComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>140</x>
|
|
||||||
<y>110</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>40</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="nameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>40</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="roleLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>190</y>
|
|
||||||
<width>67</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QLabel" name="roleNameLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>140</x>
|
|
||||||
<y>190</y>
|
|
||||||
<width>411</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="savePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>50</x>
|
|
||||||
<y>330</y>
|
|
||||||
<width>500</width>
|
|
||||||
<height>50</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,67 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>120</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>120</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>120</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="updateModeLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>5</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="updateModeComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="updatePushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -1,77 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>120</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>120</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>120</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QLabel" name="deviceLabel">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>5</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>20</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="deviceComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>30</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QPushButton" name="selectRegionPushButton">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>PushButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QComboBox" name="resolutionComboBox">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>25</x>
|
|
||||||
<y>70</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -33,14 +33,18 @@ class ProfileManager:
|
|||||||
if not os.path.exists(avatars_directory):
|
if not os.path.exists(avatars_directory):
|
||||||
os.makedirs(avatars_directory)
|
os.makedirs(avatars_directory)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Properties
|
# Properties
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_profile_saved_event(self):
|
def get_profile_saved_event(self):
|
||||||
return self._profile_saved_event
|
return self._profile_saved_event
|
||||||
|
|
||||||
profile_saved_event = property(get_profile_saved_event)
|
profile_saved_event = property(get_profile_saved_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Public methods
|
# Public methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def open_profile(self):
|
def open_profile(self):
|
||||||
with open(self._path, 'rb') as fl:
|
with open(self._path, 'rb') as fl:
|
||||||
|
@ -192,14 +192,18 @@ class Settings(dict):
|
|||||||
self.unlockScreen = False
|
self.unlockScreen = False
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Properties
|
# Properties
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def get_settings_saved_event(self):
|
def get_settings_saved_event(self):
|
||||||
return self._settings_saved_event
|
return self._settings_saved_event
|
||||||
|
|
||||||
settings_saved_event = property(get_settings_saved_event)
|
settings_saved_event = property(get_settings_saved_event)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Public methods
|
# Public methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
text = json.dumps(self)
|
text = json.dumps(self)
|
||||||
@ -248,7 +252,9 @@ class Settings(dict):
|
|||||||
self._path = new_path
|
self._path = new_path
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Static methods
|
# Static methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_auto_profile():
|
def get_auto_profile():
|
||||||
@ -381,7 +387,9 @@ class Settings(dict):
|
|||||||
}
|
}
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Private methods
|
# Private methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def _upgrade(self):
|
def _upgrade(self):
|
||||||
default = Settings.get_default_settings()
|
default = Settings.get_default_settings()
|
||||||
|
5
toxygen/wrapper/__init__.py
Normal file
5
toxygen/wrapper/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||||
|
# 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
|
87
toxygen/wrapper/libtox.py
Normal file
87
toxygen/wrapper/libtox.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from ctypes import CDLL
|
||||||
|
|
||||||
|
# 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
|
||||||
|
try:
|
||||||
|
import utils.util as util
|
||||||
|
sLIBS_DIR = util.get_libs_directory()
|
||||||
|
except ImportError:
|
||||||
|
sLIBS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)),
|
||||||
|
'libs')
|
||||||
|
|
||||||
|
# environment variable TOXCORE_LIBS overrides
|
||||||
|
d = os.environ.get('TOXCORE_LIBS', '')
|
||||||
|
if d and os.path.exists(d):
|
||||||
|
sLIBS_DIR = d
|
||||||
|
if os.environ.get('DEBUG', ''):
|
||||||
|
print ('DBUG: Setting TOXCORE_LIBS to ' +d)
|
||||||
|
del d
|
||||||
|
|
||||||
|
class LibToxCore:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
platform = sys.platform
|
||||||
|
if platform == 'win32':
|
||||||
|
libtoxcore = 'libtox.dll'
|
||||||
|
elif platform == 'darwin':
|
||||||
|
libtoxcore = 'libtoxcore.dylib'
|
||||||
|
else:
|
||||||
|
libtoxcore = 'libtoxcore.so'
|
||||||
|
|
||||||
|
# libtoxcore and libsodium may be installed in your os
|
||||||
|
# give libs/ precedence
|
||||||
|
libFile = os.path.join(sLIBS_DIR, libtoxcore)
|
||||||
|
if os.path.isfile(libFile):
|
||||||
|
self._libtoxcore = CDLL(libFile)
|
||||||
|
else:
|
||||||
|
self._libtoxcore = CDLL(libtoxcore)
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return self._libtoxcore.__getattr__(item)
|
||||||
|
|
||||||
|
class LibToxAV:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
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':
|
||||||
|
self._libtoxav = CDLL('libtoxcore.dylib')
|
||||||
|
else:
|
||||||
|
libFile = os.path.join(sLIBS_DIR, 'libtoxav.so')
|
||||||
|
if os.path.isfile(libFile):
|
||||||
|
self._libtoxav = CDLL(libFile)
|
||||||
|
else:
|
||||||
|
self._libtoxav = CDLL('libtoxav.so')
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return self._libtoxav.__getattr__(item)
|
||||||
|
|
||||||
|
# figure out how to see if we have a combined library
|
||||||
|
|
||||||
|
class LibToxEncryptSave:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
platform = sys.platform
|
||||||
|
if platform == 'win32':
|
||||||
|
# on Windows profile encryption api is in libtox.dll
|
||||||
|
self._lib_tox_encrypt_save = CDLL(os.path.join(sLIBS_DIR, 'libtox.dll'))
|
||||||
|
elif platform == 'darwin':
|
||||||
|
self._lib_tox_encrypt_save = CDLL('libtoxcore.dylib')
|
||||||
|
else:
|
||||||
|
libFile = os.path.join(sLIBS_DIR, 'libtoxencryptsave.so')
|
||||||
|
if os.path.isfile(libFile):
|
||||||
|
self._lib_tox_encrypt_save = CDLL(libFile)
|
||||||
|
else:
|
||||||
|
self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.so')
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return self._lib_tox_encrypt_save.__getattr__(item)
|
||||||
|
|
||||||
|
# figure out how to see if we have a combined library
|
80
toxygen/wrapper/tox.c-toxcore.missing
Normal file
80
toxygen/wrapper/tox.c-toxcore.missing
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
tox_version_major
|
||||||
|
tox_version_minor
|
||||||
|
tox_version_patch
|
||||||
|
tox_version_is_compatible
|
||||||
|
tox_public_key_size
|
||||||
|
tox_secret_key_size
|
||||||
|
tox_conference_uid_size
|
||||||
|
tox_conference_id_size
|
||||||
|
tox_nospam_size
|
||||||
|
tox_address_size
|
||||||
|
tox_max_name_length
|
||||||
|
tox_max_status_message_length
|
||||||
|
tox_max_friend_request_length
|
||||||
|
tox_max_message_length
|
||||||
|
tox_max_custom_packet_size
|
||||||
|
tox_hash_length
|
||||||
|
tox_file_id_length
|
||||||
|
tox_max_filename_length
|
||||||
|
tox_max_hostname_length
|
||||||
|
tox_options_get_ipv6_enabled
|
||||||
|
tox_options_get_udp_enabled
|
||||||
|
tox_options_get_local_discovery_enabled
|
||||||
|
tox_options_get_dht_announcements_enabled
|
||||||
|
tox_options_get_proxy_type
|
||||||
|
tox_options_get_proxy_port
|
||||||
|
tox_options_get_start_port
|
||||||
|
tox_options_get_end_port
|
||||||
|
tox_options_get_tcp_port
|
||||||
|
tox_options_get_hole_punching_enabled
|
||||||
|
tox_options_get_savedata_type
|
||||||
|
tox_options_get_savedata_length
|
||||||
|
tox_options_get_experimental_thread_safety
|
||||||
|
tox_file_seek
|
||||||
|
tox_callback_conference_connected
|
||||||
|
tox_callback_conference_message
|
||||||
|
tox_callback_conference_title
|
||||||
|
tox_callback_conference_peer_list_changed
|
||||||
|
tox_conference_new
|
||||||
|
tox_conference_delete
|
||||||
|
tox_conference_peer_count
|
||||||
|
tox_conference_peer_get_name_size
|
||||||
|
tox_conference_peer_get_name
|
||||||
|
tox_conference_peer_get_public_key
|
||||||
|
tox_conference_peer_number_is_ours
|
||||||
|
tox_conference_offline_peer_count
|
||||||
|
tox_conference_offline_peer_get_name_size
|
||||||
|
tox_conference_offline_peer_get_name
|
||||||
|
tox_conference_offline_peer_get_public_key
|
||||||
|
tox_conference_offline_peer_get_last_active
|
||||||
|
tox_conference_set_max_offline
|
||||||
|
tox_conference_invite
|
||||||
|
tox_conference_join
|
||||||
|
tox_conference_send_message
|
||||||
|
tox_conference_get_title_size
|
||||||
|
tox_conference_get_title
|
||||||
|
tox_conference_set_title
|
||||||
|
tox_conference_get_chatlist_size
|
||||||
|
tox_conference_get_chatlist
|
||||||
|
tox_conference_get_type
|
||||||
|
tox_conference_get_id
|
||||||
|
tox_conference_by_id
|
||||||
|
tox_conference_get_uid
|
||||||
|
tox_conference_by_uid
|
||||||
|
tox_group_max_topic_length
|
||||||
|
tox_group_max_part_length
|
||||||
|
tox_group_max_group_name_length
|
||||||
|
tox_group_max_password_size
|
||||||
|
tox_group_chat_id_size
|
||||||
|
tox_group_peer_public_key_size
|
||||||
|
tox_group_peer_get_connection_status
|
||||||
|
tox_group_get_voice_state
|
||||||
|
tox_callback_group_voice_state
|
||||||
|
tox_group_get_topic_lock
|
||||||
|
tox_callback_group_topic_lock
|
||||||
|
tox_group_send_custom_private_packet
|
||||||
|
tox_callback_group_custom_private_packet
|
||||||
|
tox_group_founder_set_topic_lock
|
||||||
|
tox_group_founder_set_voice_state
|
||||||
|
tox_group_set_ignore
|
||||||
|
tox_group_mod_kick_peer
|
3132
toxygen/wrapper/tox.py
Normal file
3132
toxygen/wrapper/tox.py
Normal file
File diff suppressed because it is too large
Load Diff
403
toxygen/wrapper/toxav.py
Normal file
403
toxygen/wrapper/toxav.py
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||||
|
|
||||||
|
from ctypes import (CFUNCTYPE, POINTER, ArgumentError, byref, c_bool, c_char_p,
|
||||||
|
c_int, c_int32, c_size_t, c_uint8, c_uint16, c_uint32,
|
||||||
|
c_void_p, cast)
|
||||||
|
|
||||||
|
from wrapper.libtox import LibToxAV
|
||||||
|
from wrapper.toxav_enums import *
|
||||||
|
|
||||||
|
|
||||||
|
def LOG_ERROR(a): print('EROR> '+a)
|
||||||
|
def LOG_WARN(a): print('WARN> '+a)
|
||||||
|
def LOG_INFO(a): print('INFO> '+a)
|
||||||
|
def LOG_DEBUG(a): print('DBUG> '+a)
|
||||||
|
def LOG_TRACE(a): pass # print('DEBUGx: '+a)
|
||||||
|
|
||||||
|
class ToxAV:
|
||||||
|
"""
|
||||||
|
The ToxAV instance type. Each ToxAV instance can be bound to only one Tox instance, and Tox instance can have only
|
||||||
|
one ToxAV instance. One must make sure to close ToxAV instance prior closing Tox instance otherwise undefined
|
||||||
|
behaviour occurs. Upon closing of ToxAV instance, all active calls will be forcibly terminated without notifying
|
||||||
|
peers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Creation and destruction
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def __init__(self, tox_pointer):
|
||||||
|
"""
|
||||||
|
Start new A/V session. There can only be only one session per Tox instance.
|
||||||
|
|
||||||
|
:param tox_pointer: pointer to Tox instance
|
||||||
|
"""
|
||||||
|
self.libtoxav = LibToxAV()
|
||||||
|
toxav_err_new = c_int()
|
||||||
|
f = self.libtoxav.toxav_new
|
||||||
|
f.restype = POINTER(c_void_p)
|
||||||
|
self._toxav_pointer = f(tox_pointer, byref(toxav_err_new))
|
||||||
|
toxav_err_new = toxav_err_new.value
|
||||||
|
if toxav_err_new == TOXAV_ERR_NEW['NULL']:
|
||||||
|
raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
|
||||||
|
elif toxav_err_new == TOXAV_ERR_NEW['MALLOC']:
|
||||||
|
raise MemoryError('Memory allocation failure while trying to allocate structures required for the A/V '
|
||||||
|
'session.')
|
||||||
|
elif toxav_err_new == TOXAV_ERR_NEW['MULTIPLE']:
|
||||||
|
raise RuntimeError('Attempted to create a second session for the same Tox instance.')
|
||||||
|
|
||||||
|
self.call_state_cb = None
|
||||||
|
self.audio_receive_frame_cb = None
|
||||||
|
self.video_receive_frame_cb = None
|
||||||
|
self.call_cb = None
|
||||||
|
|
||||||
|
def kill(self):
|
||||||
|
"""
|
||||||
|
Releases all resources associated with the A/V session.
|
||||||
|
|
||||||
|
If any calls were ongoing, these will be forcibly terminated without notifying peers. After calling this
|
||||||
|
function, no other functions may be called and the av pointer becomes invalid.
|
||||||
|
"""
|
||||||
|
self.libtoxav.toxav_kill(self._toxav_pointer)
|
||||||
|
|
||||||
|
def get_tox_pointer(self):
|
||||||
|
"""
|
||||||
|
Returns the Tox instance the A/V object was created for.
|
||||||
|
|
||||||
|
:return: pointer to the Tox instance
|
||||||
|
"""
|
||||||
|
self.libtoxav.toxav_get_tox.restype = POINTER(c_void_p)
|
||||||
|
return self.libtoxav.toxav_get_tox(self._toxav_pointer)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# A/V event loop
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def iteration_interval(self):
|
||||||
|
"""
|
||||||
|
Returns the interval in milliseconds when the next toxav_iterate call should be. If no call is active at the
|
||||||
|
moment, this function returns 200.
|
||||||
|
|
||||||
|
:return: interval in milliseconds
|
||||||
|
"""
|
||||||
|
return self.libtoxav.toxav_iteration_interval(self._toxav_pointer)
|
||||||
|
|
||||||
|
def iterate(self):
|
||||||
|
"""
|
||||||
|
Main loop for the session. This function needs to be called in intervals of toxav_iteration_interval()
|
||||||
|
milliseconds. It is best called in the separate thread from tox_iterate.
|
||||||
|
"""
|
||||||
|
self.libtoxav.toxav_iterate(self._toxav_pointer)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Call setup
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def call(self, friend_number, audio_bit_rate, video_bit_rate):
|
||||||
|
"""
|
||||||
|
Call a friend. This will start ringing the friend.
|
||||||
|
|
||||||
|
It is the client's responsibility to stop ringing after a certain timeout, if such behaviour is desired. If the
|
||||||
|
client does not stop ringing, the library will not stop until the friend is disconnected. Audio and video
|
||||||
|
receiving are both enabled by default.
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend that should be called.
|
||||||
|
:param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending.
|
||||||
|
:param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending.
|
||||||
|
:return: True on success.
|
||||||
|
"""
|
||||||
|
toxav_err_call = c_int()
|
||||||
|
LOG_DEBUG(f"toxav_call")
|
||||||
|
result = self.libtoxav.toxav_call(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate),
|
||||||
|
c_uint32(video_bit_rate), byref(toxav_err_call))
|
||||||
|
toxav_err_call = toxav_err_call.value
|
||||||
|
if toxav_err_call == TOXAV_ERR_CALL['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif toxav_err_call == TOXAV_ERR_CALL['MALLOC']:
|
||||||
|
raise MemoryError('A resource allocation error occurred while trying to create the structures required for '
|
||||||
|
'the call.')
|
||||||
|
elif toxav_err_call == TOXAV_ERR_CALL['SYNC']:
|
||||||
|
raise RuntimeError('Synchronization error occurred.')
|
||||||
|
elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend number did not designate a valid friend.')
|
||||||
|
elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_CONNECTED']:
|
||||||
|
raise ArgumentError('The friend was valid, but not currently connected.')
|
||||||
|
elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_ALREADY_IN_CALL']:
|
||||||
|
raise ArgumentError('Attempted to call a friend while already in an audio or video call with them.')
|
||||||
|
elif toxav_err_call == TOXAV_ERR_CALL['INVALID_BIT_RATE']:
|
||||||
|
raise ArgumentError('Audio or video bit rate is invalid.')
|
||||||
|
|
||||||
|
def callback_call(self, callback, user_data):
|
||||||
|
"""
|
||||||
|
Set the callback for the `call` event. Pass None to unset.
|
||||||
|
|
||||||
|
:param callback: The function for the call callback.
|
||||||
|
|
||||||
|
Should take pointer (c_void_p) to ToxAV object,
|
||||||
|
The friend number (c_uint32) from which the call is incoming.
|
||||||
|
True (c_bool) if friend is sending audio.
|
||||||
|
True (c_bool) if friend is sending video.
|
||||||
|
pointer (c_void_p) to user_data
|
||||||
|
:param user_data: pointer (c_void_p) to user data
|
||||||
|
"""
|
||||||
|
if callback is None:
|
||||||
|
self.libtoxav.toxav_callback_call(self._toxav_pointer, POINTER(None)(), user_data)
|
||||||
|
self.call_cb = None
|
||||||
|
return
|
||||||
|
LOG_DEBUG(f"toxav_callback_call")
|
||||||
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_bool, c_void_p)
|
||||||
|
self.call_cb = c_callback(callback)
|
||||||
|
self.libtoxav.toxav_callback_call(self._toxav_pointer, self.call_cb, user_data)
|
||||||
|
|
||||||
|
def answer(self, friend_number, audio_bit_rate, video_bit_rate):
|
||||||
|
"""
|
||||||
|
Accept an incoming call.
|
||||||
|
|
||||||
|
If answering fails for any reason, the call will still be pending and it is possible to try and answer it later.
|
||||||
|
Audio and video receiving are both enabled by default.
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend that is calling.
|
||||||
|
:param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending.
|
||||||
|
:param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending.
|
||||||
|
:return: True on success.
|
||||||
|
"""
|
||||||
|
toxav_err_answer = c_int()
|
||||||
|
LOG_DEBUG(f"toxav_answer")
|
||||||
|
result = self.libtoxav.toxav_answer(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate),
|
||||||
|
c_uint32(video_bit_rate), byref(toxav_err_answer))
|
||||||
|
toxav_err_answer = toxav_err_answer.value
|
||||||
|
if toxav_err_answer == TOXAV_ERR_ANSWER['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif toxav_err_answer == TOXAV_ERR_ANSWER['SYNC']:
|
||||||
|
raise RuntimeError('Synchronization error occurred.')
|
||||||
|
elif toxav_err_answer == TOXAV_ERR_ANSWER['CODEC_INITIALIZATION']:
|
||||||
|
raise RuntimeError('Failed to initialize codecs for call session. Note that codec initiation will fail if '
|
||||||
|
'there is no receive callback registered for either audio or video.')
|
||||||
|
elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend number did not designate a valid friend.')
|
||||||
|
elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_CALLING']:
|
||||||
|
raise ArgumentError('The friend was valid, but they are not currently trying to initiate a call. This is '
|
||||||
|
'also returned if this client is already in a call with the friend.')
|
||||||
|
elif toxav_err_answer == TOXAV_ERR_ANSWER['INVALID_BIT_RATE']:
|
||||||
|
raise ArgumentError('Audio or video bit rate is invalid.')
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Call state graph
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def callback_call_state(self, callback, user_data):
|
||||||
|
"""
|
||||||
|
Set the callback for the `call_state` event. Pass None to unset.
|
||||||
|
|
||||||
|
:param callback: Python function.
|
||||||
|
The function for the call_state callback.
|
||||||
|
|
||||||
|
Should take pointer (c_void_p) to ToxAV object,
|
||||||
|
The friend number (c_uint32) for which the call state changed.
|
||||||
|
The bitmask of the new call state which is guaranteed to be different than the previous state. The state is set
|
||||||
|
to 0 when the call is paused. The bitmask represents all the activities currently performed by the friend.
|
||||||
|
pointer (c_void_p) to user_data
|
||||||
|
:param user_data: pointer (c_void_p) to user data
|
||||||
|
"""
|
||||||
|
if callback is None:
|
||||||
|
self.libtoxav.toxav_callback_call_state(self._toxav_pointer, POINTER(None)(), user_data)
|
||||||
|
self.call_state_cb = None
|
||||||
|
return
|
||||||
|
LOG_DEBUG(f"callback_call_state")
|
||||||
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p)
|
||||||
|
self.call_state_cb = c_callback(callback)
|
||||||
|
self.libtoxav.toxav_callback_call_state(self._toxav_pointer, self.call_state_cb, user_data)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Call control
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def call_control(self, friend_number, control):
|
||||||
|
"""
|
||||||
|
Sends a call control command to a friend.
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend this client is in a call with.
|
||||||
|
:param control: The control command to send.
|
||||||
|
:return: True on success.
|
||||||
|
"""
|
||||||
|
toxav_err_call_control = c_int()
|
||||||
|
LOG_DEBUG(f"call_control")
|
||||||
|
result = self.libtoxav.toxav_call_control(self._toxav_pointer, c_uint32(friend_number), c_int(control),
|
||||||
|
byref(toxav_err_call_control))
|
||||||
|
toxav_err_call_control = toxav_err_call_control.value
|
||||||
|
if toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['SYNC']:
|
||||||
|
raise RuntimeError('Synchronization error occurred.')
|
||||||
|
elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
||||||
|
elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_IN_CALL']:
|
||||||
|
raise RuntimeError('This client is currently not in a call with the friend. Before the call is answered, '
|
||||||
|
'only CANCEL is a valid control.')
|
||||||
|
elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['INVALID_TRANSITION']:
|
||||||
|
raise RuntimeError('Happens if user tried to pause an already paused call or if trying to resume a call '
|
||||||
|
'that is not paused.')
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# TODO Controlling bit rates
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# A/V sending
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def audio_send_frame(self, friend_number, pcm, sample_count, channels, sampling_rate):
|
||||||
|
"""
|
||||||
|
Send an audio frame to a friend.
|
||||||
|
|
||||||
|
The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
|
||||||
|
Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
|
||||||
|
For mono audio, this has no meaning, every sample is subsequent. For stereo, this means the expected format is
|
||||||
|
LRLRLR... with samples for left and right alternating.
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend to which to send an audio frame.
|
||||||
|
:param pcm: An array of audio samples. The size of this array must be sample_count * channels.
|
||||||
|
:param sample_count: Number of samples in this frame. Valid numbers here are
|
||||||
|
((sample rate) * (audio length) / 1000), where audio length can be 2.5, 5, 10, 20, 40 or 60 milliseconds.
|
||||||
|
:param channels: Number of audio channels. Sulpported values are 1 and 2.
|
||||||
|
:param sampling_rate: Audio sampling rate used in this frame. Valid sampling rates are 8000, 12000, 16000,
|
||||||
|
24000, or 48000.
|
||||||
|
"""
|
||||||
|
toxav_err_send_frame = c_int()
|
||||||
|
LOG_TRACE(f"toxav_audio_send_frame")
|
||||||
|
assert sampling_rate in [8000, 12000, 16000, 24000, 48000]
|
||||||
|
result = self.libtoxav.toxav_audio_send_frame(self._toxav_pointer,
|
||||||
|
c_uint32(friend_number),
|
||||||
|
cast(pcm, c_void_p),
|
||||||
|
c_size_t(sample_count), c_uint8(channels),
|
||||||
|
c_uint32(sampling_rate), byref(toxav_err_send_frame))
|
||||||
|
toxav_err_send_frame = toxav_err_send_frame.value
|
||||||
|
if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']:
|
||||||
|
raise ArgumentError('The samples data pointer was NULL.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']:
|
||||||
|
raise RuntimeError('This client is currently not in a call with the friend.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']:
|
||||||
|
raise RuntimeError('Synchronization error occurred.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']:
|
||||||
|
raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too '
|
||||||
|
'large, or the audio sampling rate may be unsupported.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']:
|
||||||
|
raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said'
|
||||||
|
'payload.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']:
|
||||||
|
RuntimeError('Failed to push frame through rtp interface.')
|
||||||
|
|
||||||
|
def video_send_frame(self, friend_number, width, height, y, u, v):
|
||||||
|
"""
|
||||||
|
Send a video frame to a friend.
|
||||||
|
|
||||||
|
Y - plane should be of size: height * width
|
||||||
|
U - plane should be of size: (height/2) * (width/2)
|
||||||
|
V - plane should be of size: (height/2) * (width/2)
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend to which to send a video frame.
|
||||||
|
:param width: Width of the frame in pixels.
|
||||||
|
:param height: Height of the frame in pixels.
|
||||||
|
:param y: Y (Luminance) plane data.
|
||||||
|
:param u: U (Chroma) plane data.
|
||||||
|
:param v: V (Chroma) plane data.
|
||||||
|
"""
|
||||||
|
toxav_err_send_frame = c_int()
|
||||||
|
LOG_TRACE(f"toxav_video_send_frame")
|
||||||
|
result = self.libtoxav.toxav_video_send_frame(self._toxav_pointer, c_uint32(friend_number), c_uint16(width),
|
||||||
|
c_uint16(height), c_char_p(y), c_char_p(u), c_char_p(v),
|
||||||
|
byref(toxav_err_send_frame))
|
||||||
|
toxav_err_send_frame = toxav_err_send_frame.value
|
||||||
|
if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']:
|
||||||
|
raise ArgumentError('One of Y, U, or V was NULL.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']:
|
||||||
|
raise RuntimeError('This client is currently not in a call with the friend.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']:
|
||||||
|
raise RuntimeError('Synchronization error occurred.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']:
|
||||||
|
raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too '
|
||||||
|
'large, or the audio sampling rate may be unsupported.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']:
|
||||||
|
raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said'
|
||||||
|
'payload.')
|
||||||
|
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']:
|
||||||
|
RuntimeError('Failed to push frame through rtp interface.')
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# A/V receiving
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def callback_audio_receive_frame(self, callback, user_data):
|
||||||
|
"""
|
||||||
|
Set the callback for the `audio_receive_frame` event. Pass None to unset.
|
||||||
|
|
||||||
|
:param callback: Python function.
|
||||||
|
Function for the audio_receive_frame callback. The callback can be called multiple times per single
|
||||||
|
iteration depending on the amount of queued frames in the buffer. The received format is the same as in send
|
||||||
|
function.
|
||||||
|
|
||||||
|
Should take pointer (c_void_p) to ToxAV object,
|
||||||
|
The friend number (c_uint32) of the friend who sent an audio frame.
|
||||||
|
An array (c_uint8) of audio samples (sample_count * channels elements).
|
||||||
|
The number (c_size_t) of audio samples per channel in the PCM array.
|
||||||
|
Number (c_uint8) of audio channels.
|
||||||
|
Sampling rate (c_uint32) used in this frame.
|
||||||
|
pointer (c_void_p) to user_data
|
||||||
|
:param user_data: pointer (c_void_p) to user data
|
||||||
|
"""
|
||||||
|
if callback is None:
|
||||||
|
self.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, POINTER(None)(), user_data)
|
||||||
|
self.audio_receive_frame_cb = None
|
||||||
|
return
|
||||||
|
LOG_DEBUG(f"toxav_callback_audio_receive_frame")
|
||||||
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_uint8, c_uint32, c_void_p)
|
||||||
|
self.audio_receive_frame_cb = c_callback(callback)
|
||||||
|
self.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, self.audio_receive_frame_cb, user_data)
|
||||||
|
|
||||||
|
def callback_video_receive_frame(self, callback, user_data):
|
||||||
|
"""
|
||||||
|
Set the callback for the `video_receive_frame` event. Pass None to unset.
|
||||||
|
|
||||||
|
:param callback: Python function.
|
||||||
|
The function type for the video_receive_frame callback.
|
||||||
|
|
||||||
|
Should take
|
||||||
|
toxAV pointer (c_void_p) to ToxAV object,
|
||||||
|
friend_number The friend number (c_uint32) of the friend who sent a video frame.
|
||||||
|
width Width (c_uint16) of the frame in pixels.
|
||||||
|
height Height (c_uint16) of the frame in pixels.
|
||||||
|
y
|
||||||
|
u
|
||||||
|
v Plane data (POINTER(c_uint8)).
|
||||||
|
The size of plane data is derived from width and height where
|
||||||
|
Y = MAX(width, abs(ystride)) * height,
|
||||||
|
U = MAX(width/2, abs(ustride)) * (height/2) and
|
||||||
|
V = MAX(width/2, abs(vstride)) * (height/2).
|
||||||
|
ystride
|
||||||
|
ustride
|
||||||
|
vstride Strides data (c_int32). Strides represent padding for each plane that may or may not be present. You must
|
||||||
|
handle strides in your image processing code. Strides are negative if the image is bottom-up
|
||||||
|
hence why you MUST abs() it when calculating plane buffer size.
|
||||||
|
user_data pointer (c_void_p) to user_data
|
||||||
|
:param user_data: pointer (c_void_p) to user data
|
||||||
|
"""
|
||||||
|
if callback is None:
|
||||||
|
self.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, POINTER(None)(), user_data)
|
||||||
|
self.video_receive_frame_cb = None
|
||||||
|
return
|
||||||
|
|
||||||
|
LOG_DEBUG(f"toxav_callback_video_receive_frame")
|
||||||
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16,
|
||||||
|
POINTER(c_uint8), POINTER(c_uint8), POINTER(c_uint8),
|
||||||
|
c_int32, c_int32, c_int32,
|
||||||
|
c_void_p)
|
||||||
|
self.video_receive_frame_cb = c_callback(callback)
|
||||||
|
self.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data)
|
133
toxygen/wrapper/toxav_enums.py
Normal file
133
toxygen/wrapper/toxav_enums.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||||
|
|
||||||
|
TOXAV_ERR_NEW = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# One of the arguments to the function was NULL when it was not expected.
|
||||||
|
'NULL': 1,
|
||||||
|
# Memory allocation failure while trying to allocate structures required for the A/V session.
|
||||||
|
'MALLOC': 2,
|
||||||
|
# Attempted to create a second session for the same Tox instance.
|
||||||
|
'MULTIPLE': 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_ERR_CALL = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# A resource allocation error occurred while trying to create the structures required for the call.
|
||||||
|
'MALLOC': 1,
|
||||||
|
# Synchronization error occurred.
|
||||||
|
'SYNC': 2,
|
||||||
|
# The friend number did not designate a valid friend.
|
||||||
|
'FRIEND_NOT_FOUND': 3,
|
||||||
|
# The friend was valid, but not currently connected.
|
||||||
|
'FRIEND_NOT_CONNECTED': 4,
|
||||||
|
# Attempted to call a friend while already in an audio or video call with them.
|
||||||
|
'FRIEND_ALREADY_IN_CALL': 5,
|
||||||
|
# Audio or video bit rate is invalid.
|
||||||
|
'INVALID_BIT_RATE': 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_ERR_ANSWER = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Synchronization error occurred.
|
||||||
|
'SYNC': 1,
|
||||||
|
# Failed to initialize codecs for call session. Note that codec initiation will fail if there is no receive callback
|
||||||
|
# registered for either audio or video.
|
||||||
|
'CODEC_INITIALIZATION': 2,
|
||||||
|
# The friend number did not designate a valid friend.
|
||||||
|
'FRIEND_NOT_FOUND': 3,
|
||||||
|
# The friend was valid, but they are not currently trying to initiate a call. This is also returned if this client
|
||||||
|
# is already in a call with the friend.
|
||||||
|
'FRIEND_NOT_CALLING': 4,
|
||||||
|
# Audio or video bit rate is invalid.
|
||||||
|
'INVALID_BIT_RATE': 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_FRIEND_CALL_STATE = {
|
||||||
|
# Set by the AV core if an error occurred on the remote end or if friend timed out. This is the final state after
|
||||||
|
# which no more state transitions can occur for the call. This call state will never be triggered in combination
|
||||||
|
# with other call states.
|
||||||
|
'ERROR': 1,
|
||||||
|
# The call has finished. This is the final state after which no more state transitions can occur for the call. This
|
||||||
|
# call state will never be triggered in combination with other call states.
|
||||||
|
'FINISHED': 2,
|
||||||
|
# The flag that marks that friend is sending audio.
|
||||||
|
'SENDING_A': 4,
|
||||||
|
# The flag that marks that friend is sending video.
|
||||||
|
'SENDING_V': 8,
|
||||||
|
# The flag that marks that friend is receiving audio.
|
||||||
|
'ACCEPTING_A': 16,
|
||||||
|
# The flag that marks that friend is receiving video.
|
||||||
|
'ACCEPTING_V': 32,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_CALL_CONTROL = {
|
||||||
|
# Resume a previously paused call. Only valid if the pause was caused by this client, if not, this control is
|
||||||
|
# ignored. Not valid before the call is accepted.
|
||||||
|
'RESUME': 0,
|
||||||
|
# Put a call on hold. Not valid before the call is accepted.
|
||||||
|
'PAUSE': 1,
|
||||||
|
# Reject a call if it was not answered, yet. Cancel a call after it was answered.
|
||||||
|
'CANCEL': 2,
|
||||||
|
# Request that the friend stops sending audio. Regardless of the friend's compliance, this will cause the
|
||||||
|
# audio_receive_frame event to stop being triggered on receiving an audio frame from the friend.
|
||||||
|
'MUTE_AUDIO': 3,
|
||||||
|
# Calling this control will notify client to start sending audio again.
|
||||||
|
'UNMUTE_AUDIO': 4,
|
||||||
|
# Request that the friend stops sending video. Regardless of the friend's compliance, this will cause the
|
||||||
|
# video_receive_frame event to stop being triggered on receiving a video frame from the friend.
|
||||||
|
'HIDE_VIDEO': 5,
|
||||||
|
# Calling this control will notify client to start sending video again.
|
||||||
|
'SHOW_VIDEO': 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_ERR_CALL_CONTROL = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Synchronization error occurred.
|
||||||
|
'SYNC': 1,
|
||||||
|
# The friend_number passed did not designate a valid friend.
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
# This client is currently not in a call with the friend. Before the call is answered, only CANCEL is a valid
|
||||||
|
# control.
|
||||||
|
'FRIEND_NOT_IN_CALL': 3,
|
||||||
|
# Happens if user tried to pause an already paused call or if trying to resume a call that is not paused.
|
||||||
|
'INVALID_TRANSITION': 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_ERR_BIT_RATE_SET = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Synchronization error occurred.
|
||||||
|
'SYNC': 1,
|
||||||
|
# The audio bit rate passed was not one of the supported values.
|
||||||
|
'INVALID_AUDIO_BIT_RATE': 2,
|
||||||
|
# The video bit rate passed was not one of the supported values.
|
||||||
|
'INVALID_VIDEO_BIT_RATE': 3,
|
||||||
|
# The friend_number passed did not designate a valid friend.
|
||||||
|
'FRIEND_NOT_FOUND': 4,
|
||||||
|
# This client is currently not in a call with the friend.
|
||||||
|
'FRIEND_NOT_IN_CALL': 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOXAV_ERR_SEND_FRAME = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# In case of video, one of Y, U, or V was NULL. In case of audio, the samples data pointer was NULL.
|
||||||
|
'NULL': 1,
|
||||||
|
# The friend_number passed did not designate a valid friend.
|
||||||
|
'FRIEND_NOT_FOUND': 2,
|
||||||
|
# This client is currently not in a call with the friend.
|
||||||
|
'FRIEND_NOT_IN_CALL': 3,
|
||||||
|
# Synchronization error occurred.
|
||||||
|
'SYNC': 4,
|
||||||
|
# One of the frame parameters was invalid. E.g. the resolution may be too small or too large, or the audio sampling
|
||||||
|
# rate may be unsupported.
|
||||||
|
'INVALID': 5,
|
||||||
|
# Either friend turned off audio or video receiving or we turned off sending for the said payload.
|
||||||
|
'PAYLOAD_TYPE_DISABLED': 6,
|
||||||
|
# Failed to push frame through rtp interface.
|
||||||
|
'RTP_FAILED': 7,
|
||||||
|
}
|
957
toxygen/wrapper/toxcore_enums_and_consts.py
Normal file
957
toxygen/wrapper/toxcore_enums_and_consts.py
Normal file
@ -0,0 +1,957 @@
|
|||||||
|
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||||
|
|
||||||
|
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,
|
||||||
|
'TCP_SERVER_ALLOC': 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
#
|
||||||
|
'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.
|
||||||
|
#
|
||||||
|
'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.
|
||||||
|
#
|
||||||
|
'FOUNDER': 0,
|
||||||
|
|
||||||
|
#
|
||||||
|
# May kick, ban and set the user and observer roles for peers below this role.
|
||||||
|
# May also set the group topic.
|
||||||
|
#
|
||||||
|
'MODERATOR': 1,
|
||||||
|
|
||||||
|
#
|
||||||
|
# May communicate with other peers normally.
|
||||||
|
#
|
||||||
|
'USER': 2,
|
||||||
|
|
||||||
|
#
|
||||||
|
# May observe the group and ignore peers; may not communicate with other peers or with the group.
|
||||||
|
#
|
||||||
|
'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.
|
||||||
|
#
|
||||||
|
'KICK': 0,
|
||||||
|
|
||||||
|
#
|
||||||
|
# A peer has been banned from the group.
|
||||||
|
#
|
||||||
|
'BAN': 1,
|
||||||
|
|
||||||
|
#
|
||||||
|
# A peer as been given the observer role.
|
||||||
|
#
|
||||||
|
'OBSERVER': 2,
|
||||||
|
|
||||||
|
#
|
||||||
|
# A peer has been given the user role.
|
||||||
|
#
|
||||||
|
'USER': 3,
|
||||||
|
|
||||||
|
#
|
||||||
|
# A peer has been given the moderator role.
|
||||||
|
#
|
||||||
|
'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_GROUP_BAN_TYPE = {
|
||||||
|
|
||||||
|
'IP_PORT': 0,
|
||||||
|
|
||||||
|
'PUBLIC_KEY': 1,
|
||||||
|
|
||||||
|
'NICK': 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
|
82
toxygen/wrapper/toxencryptsave.py
Normal file
82
toxygen/wrapper/toxencryptsave.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||||
|
|
||||||
|
try:
|
||||||
|
from wrapper import libtox
|
||||||
|
from wrapper.toxencryptsave_enums_and_consts import *
|
||||||
|
except:
|
||||||
|
import libtox
|
||||||
|
from toxencryptsave_enums_and_consts import *
|
||||||
|
|
||||||
|
from ctypes import (ArgumentError, byref, c_bool, c_char_p, c_int, c_size_t,
|
||||||
|
create_string_buffer)
|
||||||
|
|
||||||
|
|
||||||
|
class ToxEncryptSave:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.libtoxencryptsave = libtox.LibToxEncryptSave()
|
||||||
|
|
||||||
|
def is_data_encrypted(self, data):
|
||||||
|
"""
|
||||||
|
Checks if given data is encrypted
|
||||||
|
"""
|
||||||
|
func = self.libtoxencryptsave.tox_is_data_encrypted
|
||||||
|
func.restype = c_bool
|
||||||
|
result = func(c_char_p(bytes(data)))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def pass_encrypt(self, data, password):
|
||||||
|
"""
|
||||||
|
Encrypts the given data with the given password.
|
||||||
|
|
||||||
|
:return: output array
|
||||||
|
"""
|
||||||
|
out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||||
|
tox_err_encryption = c_int()
|
||||||
|
self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data),
|
||||||
|
c_size_t(len(data)),
|
||||||
|
c_char_p(bytes(password, 'utf-8')),
|
||||||
|
c_size_t(len(password)),
|
||||||
|
out,
|
||||||
|
byref(tox_err_encryption))
|
||||||
|
tox_err_encryption = tox_err_encryption.value
|
||||||
|
if tox_err_encryption == TOX_ERR_ENCRYPTION['OK']:
|
||||||
|
return out[:]
|
||||||
|
elif tox_err_encryption == TOX_ERR_ENCRYPTION['NULL']:
|
||||||
|
raise ArgumentError('Some input data, or maybe the output pointer, was null.')
|
||||||
|
elif tox_err_encryption == TOX_ERR_ENCRYPTION['KEY_DERIVATION_FAILED']:
|
||||||
|
raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a'
|
||||||
|
' lack of memory issue. The functions accepting keys do not produce this error.')
|
||||||
|
elif tox_err_encryption == TOX_ERR_ENCRYPTION['FAILED']:
|
||||||
|
raise RuntimeError('The encryption itself failed.')
|
||||||
|
|
||||||
|
def pass_decrypt(self, data, password):
|
||||||
|
"""
|
||||||
|
Decrypts the given data with the given password.
|
||||||
|
|
||||||
|
:return: output array
|
||||||
|
"""
|
||||||
|
out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||||
|
tox_err_decryption = c_int()
|
||||||
|
self.libtoxencryptsave.tox_pass_decrypt(c_char_p(bytes(data)),
|
||||||
|
c_size_t(len(data)),
|
||||||
|
c_char_p(bytes(password, 'utf-8')),
|
||||||
|
c_size_t(len(password)),
|
||||||
|
out,
|
||||||
|
byref(tox_err_decryption))
|
||||||
|
tox_err_decryption = tox_err_decryption.value
|
||||||
|
if tox_err_decryption == TOX_ERR_DECRYPTION['OK']:
|
||||||
|
return out[:]
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['NULL']:
|
||||||
|
raise ArgumentError('Some input data, or maybe the output pointer, was null.')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['INVALID_LENGTH']:
|
||||||
|
raise ArgumentError('The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['BAD_FORMAT']:
|
||||||
|
raise ArgumentError('The input data is missing the magic number (i.e. wasn\'t created by this module, or is'
|
||||||
|
' corrupted)')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['KEY_DERIVATION_FAILED']:
|
||||||
|
raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a'
|
||||||
|
' lack of memory issue. The functions accepting keys do not produce this error.')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['FAILED']:
|
||||||
|
raise RuntimeError('The encrypted byte array could not be decrypted. Either the data was corrupt or the '
|
||||||
|
'password/key was incorrect.')
|
29
toxygen/wrapper/toxencryptsave_enums_and_consts.py
Normal file
29
toxygen/wrapper/toxencryptsave_enums_and_consts.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
TOX_ERR_ENCRYPTION = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Some input data, or maybe the output pointer, was null.
|
||||||
|
'NULL': 1,
|
||||||
|
# The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The
|
||||||
|
# functions accepting keys do not produce this error.
|
||||||
|
'KEY_DERIVATION_FAILED': 2,
|
||||||
|
# The encryption itself failed.
|
||||||
|
'FAILED': 3
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_DECRYPTION = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Some input data, or maybe the output pointer, was null.
|
||||||
|
'NULL': 1,
|
||||||
|
# The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes
|
||||||
|
'INVALID_LENGTH': 2,
|
||||||
|
# The input data is missing the magic number (i.e. wasn't created by this module, or is corrupted)
|
||||||
|
'BAD_FORMAT': 3,
|
||||||
|
# The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The
|
||||||
|
# functions accepting keys do not produce this error.
|
||||||
|
'KEY_DERIVATION_FAILED': 4,
|
||||||
|
# The encrypted byte array could not be decrypted. Either the data was corrupt or the password/key was incorrect.
|
||||||
|
'FAILED': 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_PASS_ENCRYPTION_EXTRA_LENGTH = 80
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user