Compare commits
53 Commits
0b4eda648e
...
main
Author | SHA1 | Date | |
---|---|---|---|
476436ab78 | |||
ffceffdc4b | |||
cbac502cbf | |||
8b3b9c87c1 | |||
283d0d2f95 | |||
6e3dd26dad | |||
11f8169e2b | |||
de1a8bdd0d | |||
3a09db186c | |||
479ae92da7 | |||
eb7287e659 | |||
4fc9a23961 | |||
fa7790eadf | |||
7759dbce8d | |||
db070348b5 | |||
477d315890 | |||
75ac0b2e07 | |||
74a29f7ce3 | |||
6aca75d2cb | |||
054e9ce8ce | |||
aef6f0d9d7 | |||
4987618fba | |||
6deea64979 | |||
a3470c1bbe | |||
1e42592027 | |||
22d8d8d8d9 | |||
6e04bdc163 | |||
c87f4a18e3 | |||
7338667e7e | |||
09502f6fc7 | |||
3efe800664 | |||
7326ed926f | |||
3acee2404a | |||
012c7ea56e | |||
b934928fe3 | |||
daee891825 | |||
bc35421760 | |||
9c9ce9fec8 | |||
da3602293a | |||
ec39c353d6 | |||
cd3b636393 | |||
d034bafd4a | |||
aedf36cda2 | |||
9111a1def8 | |||
18195f287c | |||
8ab22f32bd | |||
48b4555a04 | |||
c6282d17c6 | |||
1aa831cbd6 | |||
ad59434927 | |||
7764404b9a | |||
33734e1efd | |||
b7f6d09761 |
19
.gitea/workflows/demo.yaml
Normal file
19
.gitea/workflows/demo.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
name: Gitea Actions Demo
|
||||
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Explore-Gitea-Actions:
|
||||
runs-on: linux-amd64
|
||||
steps:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- name: List files in the repository
|
||||
run: |
|
||||
ls ${{ gitea.workspace }}
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -4,6 +4,10 @@ __pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.diff
|
||||
*.good
|
||||
*.bad
|
||||
*.junk
|
||||
*.bak
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
@ -163,3 +167,10 @@ cython_debug/
|
||||
|
||||
.pylint.err
|
||||
.pylint.log
|
||||
.pylint.out
|
||||
|
||||
*.dst
|
||||
|
||||
*~
|
||||
.rsync.sh
|
||||
.pyanal.out
|
||||
|
8
.pyanal.sh
Normal file
8
.pyanal.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
ROLE=logging
|
||||
|
||||
PYTHONPATH=$PWD/src /var/local/bin/python3.bash `which pyanalyze` \
|
||||
src/tox_wrapper/tox.py src/tox_wrapper/tests/tests_wrapper.py \
|
||||
> .pyanal.out 2>&1
|
||||
|
385
.pylint.rc
Normal file
385
.pylint.rc
Normal file
@ -0,0 +1,385 @@
|
||||
# is file was generated by edx-lint: https://github.com/edx/edx-lint
|
||||
#
|
||||
# If you want to change this file, you have two choices, depending on whether
|
||||
# you want to make a local change that applies only to this repo, or whether
|
||||
# you want to make a central change that applies to all repos using edx-lint.
|
||||
#
|
||||
# Note: If your pylintrc file is simply out-of-date relative to the latest
|
||||
# pylintrc in edx-lint, ensure you have the latest edx-lint installed
|
||||
# and then follow the steps for a "LOCAL CHANGE".
|
||||
#
|
||||
# LOCAL CHANGE:
|
||||
#
|
||||
# 1. Edit the local pylintrc_tweaks file to add changes just to this
|
||||
# repo's file.
|
||||
#
|
||||
# 2. Run:
|
||||
#
|
||||
# $ edx_lint write pylintrc
|
||||
#
|
||||
# 3. This will modify the local file. Submit a pull request to get it
|
||||
# checked in so that others will benefit.
|
||||
#
|
||||
#
|
||||
# CENTRAL CHANGE:
|
||||
#
|
||||
# 1. Edit the pylintrc file in the edx-lint repo at
|
||||
# https://github.com/edx/edx-lint/blob/master/edx_lint/files/pylintrc
|
||||
#
|
||||
# 2. install the updated version of edx-lint (in edx-lint):
|
||||
#
|
||||
# $ pip install .
|
||||
#
|
||||
# 3. Run (in edx-lint):
|
||||
#
|
||||
# $ edx_lint write pylintrc
|
||||
#
|
||||
# 4. Make a new version of edx_lint, submit and review a pull request with the
|
||||
# pylintrc update, and after merging, update the edx-lint version and
|
||||
# publish the new version.
|
||||
#
|
||||
# 5. In your local repo, install the newer version of edx-lint.
|
||||
#
|
||||
# 6. Run:
|
||||
#
|
||||
# $ edx_lint write pylintrc
|
||||
#
|
||||
# 7. This will modify the local file. Submit a pull request to get it
|
||||
# checked in so that others will benefit.
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# STAY AWAY FROM THIS FILE!
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# SERIOUSLY.
|
||||
#
|
||||
# ------------------------------
|
||||
# Generated by edx-lint version: 5.2.3
|
||||
# ------------------------------
|
||||
[MASTER]
|
||||
ignore = ,input
|
||||
persistent = yes
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
enable =
|
||||
blacklisted-name,
|
||||
# line-too-long,
|
||||
|
||||
abstract-class-instantiated,
|
||||
abstract-method,
|
||||
access-member-before-definition,
|
||||
anomalous-backslash-in-string,
|
||||
anomalous-unicode-escape-in-string,
|
||||
arguments-differ,
|
||||
assert-on-tuple,
|
||||
assigning-non-slot,
|
||||
assignment-from-no-return,
|
||||
assignment-from-none,
|
||||
attribute-defined-outside-init,
|
||||
bad-except-order,
|
||||
bad-format-character,
|
||||
bad-format-string-key,
|
||||
bad-format-string,
|
||||
bad-open-mode,
|
||||
bad-reversed-sequence,
|
||||
bad-staticmethod-argument,
|
||||
bad-str-strip-call,
|
||||
bad-super-call,
|
||||
binary-op-exception,
|
||||
boolean-datetime,
|
||||
catching-non-exception,
|
||||
cell-var-from-loop,
|
||||
confusing-with-statement,
|
||||
continue-in-finally,
|
||||
dangerous-default-value,
|
||||
duplicate-argument-name,
|
||||
duplicate-bases,
|
||||
duplicate-except,
|
||||
duplicate-key,
|
||||
expression-not-assigned,
|
||||
format-combined-specification,
|
||||
format-needs-mapping,
|
||||
function-redefined,
|
||||
global-variable-undefined,
|
||||
import-error,
|
||||
import-self,
|
||||
inconsistent-mro,
|
||||
inherit-non-class,
|
||||
init-is-generator,
|
||||
invalid-all-object,
|
||||
invalid-format-index,
|
||||
invalid-length-returned,
|
||||
invalid-sequence-index,
|
||||
invalid-slice-index,
|
||||
invalid-slots-object,
|
||||
invalid-slots,
|
||||
invalid-unary-operand-type,
|
||||
logging-too-few-args,
|
||||
logging-too-many-args,
|
||||
logging-unsupported-format,
|
||||
lost-exception,
|
||||
method-hidden,
|
||||
misplaced-bare-raise,
|
||||
misplaced-future,
|
||||
missing-format-argument-key,
|
||||
missing-format-attribute,
|
||||
missing-format-string-key,
|
||||
no-member,
|
||||
no-method-argument,
|
||||
no-name-in-module,
|
||||
no-self-argument,
|
||||
no-value-for-parameter,
|
||||
non-iterator-returned,
|
||||
nonexistent-operator,
|
||||
not-a-mapping,
|
||||
not-an-iterable,
|
||||
not-callable,
|
||||
not-context-manager,
|
||||
not-in-loop,
|
||||
pointless-statement,
|
||||
pointless-string-statement,
|
||||
raising-bad-type,
|
||||
raising-non-exception,
|
||||
redefined-builtin,
|
||||
redefined-outer-name,
|
||||
redundant-keyword-arg,
|
||||
repeated-keyword,
|
||||
return-arg-in-generator,
|
||||
return-in-init,
|
||||
return-outside-function,
|
||||
signature-differs,
|
||||
super-init-not-called,
|
||||
syntax-error,
|
||||
too-few-format-args,
|
||||
too-many-format-args,
|
||||
too-many-function-args,
|
||||
truncated-format-string,
|
||||
undefined-all-variable,
|
||||
undefined-loop-variable,
|
||||
undefined-variable,
|
||||
unexpected-keyword-arg,
|
||||
unexpected-special-method-signature,
|
||||
unpacking-non-sequence,
|
||||
unreachable,
|
||||
unsubscriptable-object,
|
||||
unsupported-binary-operation,
|
||||
unsupported-membership-test,
|
||||
unused-format-string-argument,
|
||||
unused-format-string-key,
|
||||
used-before-assignment,
|
||||
using-constant-test,
|
||||
yield-outside-function,
|
||||
|
||||
astroid-error,
|
||||
fatal,
|
||||
method-check-failed,
|
||||
parse-error,
|
||||
raw-checker-failed,
|
||||
|
||||
empty-docstring,
|
||||
invalid-characters-in-docstring,
|
||||
# missing-docstring,
|
||||
# wrong-spelling-in-comment,
|
||||
# wrong-spelling-in-docstring,
|
||||
|
||||
unused-argument,
|
||||
unused-import,
|
||||
unused-variable,
|
||||
|
||||
eval-used,
|
||||
exec-used,
|
||||
|
||||
bad-classmethod-argument,
|
||||
bad-mcs-classmethod-argument,
|
||||
bad-mcs-method-argument,
|
||||
bare-except,
|
||||
broad-except,
|
||||
consider-iterating-dictionary,
|
||||
consider-using-enumerate,
|
||||
global-variable-not-assigned,
|
||||
logging-format-interpolation,
|
||||
# logging-not-lazy,
|
||||
multiple-imports,
|
||||
multiple-statements,
|
||||
no-classmethod-decorator,
|
||||
no-staticmethod-decorator,
|
||||
protected-access,
|
||||
redundant-unittest-assert,
|
||||
reimported,
|
||||
simplifiable-if-statement,
|
||||
singleton-comparison,
|
||||
superfluous-parens,
|
||||
unidiomatic-typecheck,
|
||||
unnecessary-lambda,
|
||||
unnecessary-pass,
|
||||
unnecessary-semicolon,
|
||||
unneeded-not,
|
||||
useless-else-on-loop,
|
||||
|
||||
deprecated-method,
|
||||
deprecated-module,
|
||||
|
||||
too-many-boolean-expressions,
|
||||
too-many-nested-blocks,
|
||||
too-many-statements,
|
||||
|
||||
# wildcard-import,
|
||||
# wrong-import-order,
|
||||
# wrong-import-position,
|
||||
|
||||
missing-final-newline,
|
||||
mixed-line-endings,
|
||||
trailing-newlines,
|
||||
# trailing-whitespace,
|
||||
unexpected-line-ending-format,
|
||||
|
||||
bad-inline-option,
|
||||
bad-option-value,
|
||||
deprecated-pragma,
|
||||
unrecognized-inline-option,
|
||||
useless-suppression,
|
||||
disable =
|
||||
global-at-module-level,
|
||||
useless-return,
|
||||
#
|
||||
bad-indentation,
|
||||
consider-using-f-string,
|
||||
duplicate-code,
|
||||
f-string-without-interpolation,
|
||||
file-ignored,
|
||||
fixme,
|
||||
global-statement,
|
||||
invalid-name,
|
||||
locally-disabled,
|
||||
no-else-return,
|
||||
## no-self-use,
|
||||
suppressed-message,
|
||||
too-few-public-methods,
|
||||
too-many-ancestors,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-public-methods,
|
||||
too-many-return-statements,
|
||||
ungrouped-imports,
|
||||
unspecified-encoding,
|
||||
unused-wildcard-import,
|
||||
use-maxsplit-arg,
|
||||
wrong-import-order,
|
||||
wrong-import-position,
|
||||
|
||||
logging-not-lazy,
|
||||
line-too-long,
|
||||
import-outside-toplevel,
|
||||
logging-fstring-interpolation,
|
||||
# new
|
||||
missing-module-docstring,
|
||||
missing-class-docstring,
|
||||
missing-function-docstring,
|
||||
use-dict-literal,
|
||||
|
||||
[REPORTS]
|
||||
output-format = text
|
||||
##files-output = no
|
||||
reports = no
|
||||
score = no
|
||||
|
||||
[BASIC]
|
||||
##bad-functions = map,filter,apply,input
|
||||
module-rgx = (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
const-rgx = (([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$
|
||||
class-rgx = [A-Z_][a-zA-Z0-9]+$
|
||||
function-rgx = ([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$
|
||||
method-rgx = ([a-z_][a-z0-9_]{2,40}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*|maxDiff|test_[a-z0-9_]+)$
|
||||
attr-rgx = [a-z_][a-z0-9_]{2,30}$
|
||||
argument-rgx = [a-z_][a-z0-9_]{2,30}$
|
||||
variable-rgx = [a-z_][a-z0-9_]{2,30}$
|
||||
class-attribute-rgx = ([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||
inlinevar-rgx = [A-Za-z_][A-Za-z0-9_]*$
|
||||
good-names = f,i,j,k,db,ex,Run,_,__
|
||||
bad-names = foo,bar,baz,toto,tutu,tata
|
||||
no-docstring-rgx = __.*__$|test_.+|setUp$|setUpClass$|tearDown$|tearDownClass$|Meta$
|
||||
docstring-min-length = 5
|
||||
|
||||
[FORMAT]
|
||||
max-line-length = 120
|
||||
ignore-long-lines = ^\s*(# )?((<?https?://\S+>?)|(\.\. \w+: .*))$
|
||||
single-line-if-stmt = no
|
||||
##no-space-check = trailing-comma,dict-separator
|
||||
max-module-lines = 1000
|
||||
indent-string = ' '
|
||||
|
||||
[MISCELLANEOUS]
|
||||
notes = FIXME,XXX,TODO
|
||||
|
||||
[SIMILARITIES]
|
||||
min-similarity-lines = 4
|
||||
ignore-comments = yes
|
||||
ignore-docstrings = yes
|
||||
ignore-imports = no
|
||||
|
||||
[TYPECHECK]
|
||||
ignore-mixin-members = yes
|
||||
ignored-classes = SQLObject
|
||||
unsafe-load-any-extension = yes
|
||||
generated-members =
|
||||
REQUEST,
|
||||
acl_users,
|
||||
aq_parent,
|
||||
objects,
|
||||
DoesNotExist,
|
||||
can_read,
|
||||
can_write,
|
||||
get_url,
|
||||
size,
|
||||
content,
|
||||
status_code,
|
||||
create,
|
||||
build,
|
||||
fields,
|
||||
tag,
|
||||
org,
|
||||
course,
|
||||
category,
|
||||
name,
|
||||
revision,
|
||||
_meta,
|
||||
|
||||
[VARIABLES]
|
||||
init-import = no
|
||||
dummy-variables-rgx = _|dummy|unused|.*_unused
|
||||
additional-builtins =
|
||||
|
||||
[CLASSES]
|
||||
defining-attr-methods = __init__,__new__,setUp
|
||||
valid-classmethod-first-arg = cls
|
||||
valid-metaclass-classmethod-first-arg = mcs
|
||||
|
||||
[DESIGN]
|
||||
max-args = 5
|
||||
ignored-argument-names = _.*
|
||||
max-locals = 15
|
||||
max-returns = 9
|
||||
max-branches = 12
|
||||
max-statements = 50
|
||||
max-parents = 7
|
||||
max-attributes = 7
|
||||
min-public-methods = 2
|
||||
max-public-methods = 20
|
||||
|
||||
[IMPORTS]
|
||||
deprecated-modules = regsub,TERMIOS,Bastion,rexec
|
||||
import-graph =
|
||||
ext-import-graph =
|
||||
int-import-graph =
|
||||
|
||||
[EXCEPTIONS]
|
||||
overgeneral-exceptions = BaseException
|
22
.pylint.sh
Normal file
22
.pylint.sh
Normal file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
EXE=/usr/local/bin/toxcore_pylint.bash
|
||||
ROLE=toxcore
|
||||
|
||||
$EXE --recursive y --verbose --py-version 3.11 \
|
||||
--output-format colorized --rcfile .pylint.rc \
|
||||
-E -f text src/tox_wrapper/*py src/tox_wrapper/tests/*py > .pylint.err
|
||||
retval=$?
|
||||
|
||||
$EXE --recursive y --verbose --py-version 3.11 \
|
||||
--output-format colorized --rcfile .pylint.rc \
|
||||
src/tox_wrapper/*py src/tox_wrapper/tests/*py > .pylint.out
|
||||
|
||||
sed -e "/Module 'os' has no/d" \
|
||||
-e "/Undefined variable 'app'/d" \
|
||||
-e '/tests\//d' \
|
||||
-e "/Instance of 'Curl' has no /d" \
|
||||
-e "/No name 'path' in module 'os' /d" \
|
||||
-e "/ in module 'os'/d" \
|
||||
-e "/.bak\//d" \
|
||||
-i .pylint.err .pylint.out
|
16
LICENSE
Normal file
16
LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (c) year copyright holder. 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.
|
||||
Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2.
|
||||
Redistribution 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 the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR 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, WHETHER IN CONTRACT, STRICT 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 DAMAGE.
|
||||
|
||||
YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY MILITARY FACILITY.
|
61
Makefile
Normal file
61
Makefile
Normal file
@ -0,0 +1,61 @@
|
||||
PREFIX=/usr/local
|
||||
PYTHON_EXE_MSYS=${PREFIX}/bin/python3.sh
|
||||
PIP_EXE_MSYS=${PREFIX}/bin/pip3.sh
|
||||
|
||||
iTEST_TIMEOUT=60
|
||||
fSOCKET_TIMEOUT=15.0
|
||||
PYTHON_MINOR=`python3 --version 2>&1 | sed -e 's@^.* @@' -e 's@\.[0-9]*$$@@'`
|
||||
|
||||
prepare::
|
||||
bash .pylint.sh
|
||||
|
||||
check::
|
||||
PYTHONPATH=$${PWD}/src pyanalyze \
|
||||
src/toxygen_wrapper/tox.py src/toxygen_wrapper/tests/tests_wrapper.py \
|
||||
> .pyanal.out 2>&1
|
||||
|
||||
install::
|
||||
${PIP_EXE_MSYS} --python ${PYTHON_EXE_MSYS} install \
|
||||
--no-deps \
|
||||
--target ${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/ \
|
||||
--upgrade .
|
||||
sed -i -e "1s@/usr/bin/python${PYTHON_MINOR}@${PYTHON_EXE_MSYS}@" \
|
||||
${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/bin/*
|
||||
|
||||
rsync::
|
||||
bash .rsync.sh
|
||||
|
||||
help::
|
||||
PYTHONPATH=$${PWD}/src \
|
||||
$(PYTHON) src/toxygen_wrapper/tests/tests_wrapper.py --help
|
||||
|
||||
test:: test_proxy
|
||||
test_direct::
|
||||
cp -p ${HOME}/.config/tox/DHTnodes.json /tmp/toxygen_nodes.json||true
|
||||
PYTHONPATH=$${PWD}/src \
|
||||
TOR_CONTROLLER_PASSWORD=${PASS} \
|
||||
$(PYTHON_EXE_MSYS) src/toxygen_wrapper/tests/tests_wrapper.py \
|
||||
--norequest=True \
|
||||
--socket_timeout=10.0 \
|
||||
--test_timeout=${iTEST_TIMEOUT} \
|
||||
--nodes_json=/tmp/toxygen_nodes.json \
|
||||
--udp_enabled=True \
|
||||
--trace_enabled=False --loglevel=10
|
||||
|
||||
test_proxy::
|
||||
PYTHONPATH=$${PWD}/src \
|
||||
TOR_CONTROLLER_PASSWORD=${PASS} \
|
||||
${PYTHON_EXE_MSYS} src/toxygen_wrapper/tests/tests_wrapper.py \
|
||||
--norequest=True \
|
||||
--socket_timeout=15.0 \
|
||||
--test_timeout=${iTEST_TIMEOUT} \
|
||||
--proxy_host=127.0.0.1 \
|
||||
--proxy_port=9050 \
|
||||
--proxy_type=2 \
|
||||
--nodes_json=$$HOME/.config/tox/DHTnodes.json \
|
||||
--trace_enabled=False \
|
||||
--loglevel=10
|
||||
|
||||
clean::
|
||||
rm -f .[a-z]*~ *~ */*~ */*/*~
|
||||
rm -rf *.egg-info
|
78
README.md
78
README.md
@ -4,34 +4,40 @@
|
||||
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.
|
||||
|
||||
<https://github.com/toxygen-project/toxygen> ```next_gen``` branch by Ingvar.
|
||||
|
||||
There is not complete coverage of the c-toxcore api - they're written to support
|
||||
the [toxygeb](https://git.plastiras.org/emdee/toxygen) client.
|
||||
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.
|
||||
and the list of still unwrapped calls as of Feb. 2024 can be found in
|
||||
```docs/tox.c-toxcore.missing```. The code is typed so that every call in
|
||||
```tox*.py``` should have the right signature, and 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```.
|
||||
easy to fix. There is a good coverage integration testsuite in ```toxygen_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.
|
||||
Edit the Makefile and run ```make install``` or ```cd src```
|
||||
and run ```toxygen_wrapper/tests/tests_wrapper.py```
|
||||
|
||||
Then you need a ```libs``` directory beside the `wrapper` directory
|
||||
Then you need a ```libs``` directory beside the ```toxygen_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```.
|
||||
the environment variable TOXCORE_LIBS overrides the location of ```libs```;
|
||||
look in the file ```toxygen_wrapper/libtox.py``` for the details.
|
||||
|
||||
# Tests
|
||||
|
||||
To test, run ```python3 src/toxygen_wrapper/tests/tests_wrapper.py --help```
|
||||
|
||||
As is, the code in ```tox.py``` is very verbose. Edit the file to change
|
||||
```
|
||||
@ -44,12 +50,21 @@ 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```.
|
||||
```toxygen_wrapper/tests/tests_wrapper.py```.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
No prerequisites in Python3.
|
||||
|
||||
Because this is using Ctypes, you can run it under a python-enabled gdb,
|
||||
which if you compiled the c-toxcore library ```-DCMAKE_BUILD_TYPE="Debug"```
|
||||
means that you can run both the Python and the C under gdb. This is HUGE!
|
||||
The incantation is something like this:
|
||||
```
|
||||
gdb -ex r --args /usr/bin/python3 src/toxygen_wrapper/tests/tests_wrapper.py
|
||||
```
|
||||
with some suitable settings of PYTHONPATH and maybe LD_LIBRARY_PATH.
|
||||
|
||||
## Other wrappers
|
||||
|
||||
There are a number of other wrappings into Python of Tox core.
|
||||
@ -70,18 +85,43 @@ 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.
|
||||
java, go, etc. bindings instead, based on a homebrew generator written
|
||||
in undocumented, uncommented code in a language almost nobody knows, that
|
||||
nobody has installed, written by anonymous coders that are not open to suggestions.
|
||||
There's no active support by ```gdb`` for debugging Cython and python together
|
||||
like there is for cmake and ```gdb```. These bindings have no support for NGC
|
||||
groups; and no significant tests.
|
||||
|
||||
* <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.
|
||||
No support for NGC but good tests. Abandonned.
|
||||
This was the basis for the TokTok/py-toxcore-c code until recently,
|
||||
and a version is on the 0.2.0 branch of
|
||||
[TokTok/py-toxcore-c](https://github.com/TokTok/py-toxcore-c)
|
||||
|
||||
To our point of view, the ability of CTYPEs to follow code in the
|
||||
debugger is a crucial advantage.
|
||||
To our point of view, the ability of these ```ctypes``` to follow code python and C
|
||||
code in the debugger is a crucial advantage.
|
||||
|
||||
## Updates
|
||||
|
||||
Although Tox works over Tor, we do not recommend its usage for
|
||||
anonymity as it leaks DNS requests due to a 6-year old known security
|
||||
issue: https://github.com/TokTok/c-toxcore/issues/469 unless your Tox client
|
||||
does hostname lookups before calling Tox (like toxygen does). Otherwise,
|
||||
do not use it for anonymous communication unless you have a firewall in place.
|
||||
|
||||
The Tox project does not follow semantic versioning of its main structures
|
||||
or setters so the project may break the underlying ctypes wrapper at any time;
|
||||
it's not possible to use Tox version numbers to tell what the API will be.
|
||||
In which case you may have to go into the tox.py file in
|
||||
https://git.plastiras.org/emdee/toxygen_wrapper to fix it yourself.
|
||||
The last tested git commit is 5dd9ee3f65423a4095cacb8396a5d406a27610c7
|
||||
2024-02-10
|
||||
|
||||
Up-to-date code is on https://git.plastiras.org/emdee/toxygen_wrapper
|
||||
|
||||
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!
|
||||
|
||||
|
116
docs/c-toxcore.missing
Normal file
116
docs/c-toxcore.missing
Normal file
@ -0,0 +1,116 @@
|
||||
tox_callback_conference_connected
|
||||
tox_callback_conference_invite
|
||||
tox_callback_conference_message
|
||||
tox_callback_conference_peer_list_changed
|
||||
tox_callback_conference_peer_name
|
||||
tox_callback_conference_title
|
||||
tox_callback_group_custom_private_packet
|
||||
tox_conference_by_id
|
||||
tox_conference_by_uid
|
||||
tox_conference_delete
|
||||
tox_conference_get_chatlist
|
||||
tox_conference_get_chatlist_size
|
||||
tox_conference_get_id
|
||||
tox_conference_get_title
|
||||
tox_conference_get_title_size
|
||||
tox_conference_get_type
|
||||
tox_conference_get_uid
|
||||
tox_conference_id_size
|
||||
tox_conference_invite
|
||||
tox_conference_join
|
||||
tox_conference_new
|
||||
tox_conference_offline_peer_count
|
||||
tox_conference_offline_peer_get_last_active
|
||||
tox_conference_offline_peer_get_name
|
||||
tox_conference_offline_peer_get_name_size
|
||||
tox_conference_offline_peer_get_public_key
|
||||
tox_conference_peer_count
|
||||
tox_conference_peer_get_name
|
||||
tox_conference_peer_get_name_size
|
||||
tox_conference_peer_get_public_key
|
||||
tox_conference_peer_number_is_ours
|
||||
tox_conference_send_message
|
||||
tox_conference_set_max_offline
|
||||
tox_conference_set_title
|
||||
tox_conference_uid_size
|
||||
tox_file_seek
|
||||
tox_get_salt
|
||||
tox_group_send_custom_private_packet
|
||||
tox_is_data_encrypted
|
||||
tox_options_get_dht_announcements_enabled
|
||||
tox_options_get_end_port
|
||||
tox_options_get_experimental_groups_persistence
|
||||
tox_options_get_experimental_thread_safety
|
||||
tox_options_get_hole_punching_enabled
|
||||
tox_options_get_ipv6_enabled
|
||||
tox_options_get_local_discovery_enabled
|
||||
tox_options_get_log_callback
|
||||
tox_options_get_log_user_data
|
||||
tox_options_get_operating_system
|
||||
tox_options_get_proxy_host
|
||||
tox_options_get_proxy_port
|
||||
tox_options_get_proxy_type
|
||||
tox_options_get_savedata_data
|
||||
tox_options_get_savedata_length
|
||||
tox_options_get_savedata_type
|
||||
tox_options_get_start_port
|
||||
tox_options_get_tcp_port
|
||||
tox_options_get_udp_enabled
|
||||
tox_options_set_dht_announcements_enabled
|
||||
tox_options_set_end_port
|
||||
tox_options_set_experimental_groups_persistence
|
||||
tox_options_set_experimental_thread_safety
|
||||
tox_options_set_hole_punching_enabled
|
||||
tox_options_set_ipv6_enabled
|
||||
tox_options_set_local_discovery_enabled
|
||||
tox_options_set_log_callback
|
||||
tox_options_set_log_user_data
|
||||
tox_options_set_operating_system
|
||||
tox_options_set_proxy_host
|
||||
tox_options_set_proxy_port
|
||||
tox_options_set_proxy_type
|
||||
tox_options_set_savedata_data
|
||||
tox_options_set_savedata_length
|
||||
tox_options_set_savedata_type
|
||||
tox_options_set_start_port
|
||||
tox_options_set_tcp_port
|
||||
tox_options_set_udp_enabled
|
||||
tox_pass_decrypt
|
||||
tox_pass_encrypt
|
||||
tox_pass_encryption_extra_length
|
||||
tox_pass_key_decrypt
|
||||
tox_pass_key_derive
|
||||
tox_pass_key_derive_with_salt
|
||||
tox_pass_key_encrypt
|
||||
tox_pass_key_free
|
||||
tox_pass_key_length
|
||||
tox_pass_salt_length
|
||||
tox_version_is_compatible
|
||||
toxav_add_av_groupchat
|
||||
toxav_answer
|
||||
toxav_audio_iterate
|
||||
toxav_audio_iteration_interval
|
||||
toxav_audio_send_frame
|
||||
toxav_audio_set_bit_rate
|
||||
toxav_call
|
||||
toxav_call_control
|
||||
toxav_callback_audio_bit_rate
|
||||
toxav_callback_audio_receive_frame
|
||||
toxav_callback_call
|
||||
toxav_callback_call_state
|
||||
toxav_callback_video_bit_rate
|
||||
toxav_callback_video_receive_frame
|
||||
toxav_get_tox
|
||||
toxav_group_send_audio
|
||||
toxav_groupchat_av_enabled
|
||||
toxav_groupchat_disable_av
|
||||
toxav_groupchat_enable_av
|
||||
toxav_iterate
|
||||
toxav_iteration_interval
|
||||
toxav_join_av_groupchat
|
||||
toxav_kill
|
||||
toxav_new
|
||||
toxav_video_iterate
|
||||
toxav_video_iteration_interval
|
||||
toxav_video_send_frame
|
||||
toxav_video_set_bit_rate
|
8947
docs/toktok.ltd/spec.html
Normal file
8947
docs/toktok.ltd/spec.html
Normal file
File diff suppressed because it is too large
Load Diff
42
pyproject.toml
Normal file
42
pyproject.toml
Normal file
@ -0,0 +1,42 @@
|
||||
[project]
|
||||
name = "toxygen_wrapper"
|
||||
description = "A Python3 ctypes wrapping of c-toxcore into Python."
|
||||
authors = [{ name = "Ingvar", email = "Ingvar@gitgub.com" } ]
|
||||
requires-python = ">3.7"
|
||||
keywords = ["tox", "python3", "ctypes"]
|
||||
classifiers = [
|
||||
"License :: OSI Approved",
|
||||
"Operating System :: POSIX :: BSD :: FreeBSD",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
]
|
||||
dynamic = ["version", "readme", ] # cannot be dynamic ['license']
|
||||
|
||||
[project.scripts]
|
||||
toxygen_wrapper_tests = "toxygen_wrapper.tests.tests_wrapper:main"
|
||||
toxygen_echo = "toxygen_wrapper.toxygen_echo:main"
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "toxygen_wrapper.__version__"}
|
||||
readme = {file = ["README.md"]}
|
||||
|
||||
[project.license]
|
||||
file = "LICENSE.md"
|
||||
|
||||
[project.urls]
|
||||
repository = "https://git.plastiras.org/emdee/toxygen_wrapper"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools >= 61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["toxygen_wrapper", "toxygen_wrapper.tests"]
|
||||
|
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# the versions are the current ones tested - may work with earlier versions
|
||||
toxygen_wrapper >= 1.0.0
|
||||
yaml
|
||||
msgpack
|
||||
coloredlogs
|
||||
# nmap
|
57
setup.cfg
Normal file
57
setup.cfg
Normal file
@ -0,0 +1,57 @@
|
||||
[metadata]
|
||||
classifiers =
|
||||
License :: OSI Approved
|
||||
Intended Audience :: Web Developers
|
||||
Operating System :: POSIX :: BSD :: FreeBSD
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
description='Tox ctypes wrapping into Python'
|
||||
long_description='Tox ctypes wrapping of c-toxcore into Python3'
|
||||
url='https://git.plastiras.org/emdee/toxygen_wrapper/'
|
||||
keywords='ctypes Tox messenger'
|
||||
|
||||
[options]
|
||||
zip_safe = false
|
||||
python_requires = >=3.6
|
||||
package_dir=
|
||||
=src
|
||||
packages = ["toxygen_wrapper", "toxygen_wrapper.tests"]
|
||||
include_package_data =
|
||||
"*" = ["*.txt"]
|
||||
install_requires =
|
||||
coloredlogs
|
||||
|
||||
[easy_install]
|
||||
zip_ok = false
|
||||
|
||||
[flake8]
|
||||
jobs = 1
|
||||
max-line-length = 88
|
||||
ignore =
|
||||
E111
|
||||
E114
|
||||
E128
|
||||
E225
|
||||
E261
|
||||
E302
|
||||
E305
|
||||
E402
|
||||
E501
|
||||
E502
|
||||
E541
|
||||
E701
|
||||
E702
|
||||
E704
|
||||
E722
|
||||
E741
|
||||
F508
|
||||
F541
|
||||
W503
|
||||
W601
|
33
setup.py
Normal file
33
setup.py
Normal file
@ -0,0 +1,33 @@
|
||||
import sys
|
||||
import os
|
||||
from setuptools import setup
|
||||
from setuptools.command.install import install
|
||||
|
||||
version = '1.0.0'
|
||||
|
||||
setup(name='tox_wrapper',
|
||||
version=version,
|
||||
description='Tox ctypes wrapping into Python',
|
||||
long_description='Tox ctypes wrapping of c-toxcore into Python3',
|
||||
url='https://git.plastiras.org/emdee/toxygen/',
|
||||
keywords='ctypes Tox messenger',
|
||||
author='Ingvar',
|
||||
maintainer='',
|
||||
license='GPL3',
|
||||
packages=['tox_wrapper'],
|
||||
install_requires=['ctypes'],
|
||||
include_package_data=True,
|
||||
# dont run directly if you need a proxy
|
||||
# run python3 tox_wrapper/tests/tests_wrapper.py --help
|
||||
test_suite="tox_wrapper.tests.tests_wrapper.main",
|
||||
classifiers=[
|
||||
"Environment :: Console",
|
||||
"Topic :: Internet",
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved",
|
||||
],
|
||||
zip_safe=False
|
||||
)
|
7
src/toxygen_wrapper/__init__.py
Normal file
7
src/toxygen_wrapper/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- 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
|
||||
|
||||
__version__ = "1.0.0"
|
@ -18,7 +18,7 @@ except ImportError:
|
||||
d = os.environ.get('TOXCORE_LIBS', '')
|
||||
if d and os.path.exists(d):
|
||||
sLIBS_DIR = d
|
||||
if os.environ.get('DEBUG', ''):
|
||||
if os.environ.get('DEBUG', ''):
|
||||
print ('DBUG: Setting TOXCORE_LIBS to ' +d)
|
||||
del d
|
||||
|
@ -90,7 +90,7 @@ _socks4errors = ("request granted",
|
||||
"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):
|
||||
def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None) -> None:
|
||||
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
||||
Sets a default proxy which all further socksocket objects will use,
|
||||
unless explicitly changed.
|
||||
@ -98,7 +98,7 @@ def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=No
|
||||
global _defaultproxy
|
||||
_defaultproxy = (proxytype, addr, port, rdns, username, password)
|
||||
|
||||
def wrapmodule(module):
|
||||
def wrapmodule(module) -> None:
|
||||
"""wrapmodule(module)
|
||||
Attempts to replace a module's socket library with a SOCKS socket. Must set
|
||||
a default proxy using setdefaultproxy(...) first.
|
||||
@ -272,7 +272,7 @@ class socksocket(socket.socket):
|
||||
"""
|
||||
return self.__proxypeername
|
||||
|
||||
def __negotiatesocks4(self,destaddr,destport):
|
||||
def __negotiatesocks4(self,destaddr,destport) -> None:
|
||||
"""__negotiatesocks4(self,destaddr,destport)
|
||||
Negotiates a connection through a SOCKS4 server.
|
||||
"""
|
||||
@ -320,7 +320,7 @@ class socksocket(socket.socket):
|
||||
else:
|
||||
self.__proxypeername = (destaddr, destport)
|
||||
|
||||
def __negotiatehttp(self, destaddr, destport):
|
||||
def __negotiatehttp(self, destaddr, destport) -> None:
|
||||
"""__negotiatehttp(self,destaddr,destport)
|
||||
Negotiates a connection through an HTTP server.
|
||||
"""
|
||||
@ -354,7 +354,7 @@ class socksocket(socket.socket):
|
||||
self.__proxysockname = ("0.0.0.0", 0)
|
||||
self.__proxypeername = (addr, destport)
|
||||
|
||||
def connect(self, destpair):
|
||||
def connect(self, destpair) -> None:
|
||||
"""connect(self, despair)
|
||||
Connects to the specified destination through a proxy.
|
||||
destpar - A tuple of the IP/DNS address and the port number.
|
@ -8,7 +8,7 @@ import urllib
|
||||
import traceback
|
||||
|
||||
global LOG
|
||||
LOG = logging.getLogger('app.'+'ts')
|
||||
LOG = logging.getLogger('TestS')
|
||||
|
||||
try:
|
||||
import pycurl
|
||||
@ -22,8 +22,8 @@ except ImportError:
|
||||
lNO_PROXY = ['localhost', '127.0.0.1']
|
||||
CONNECT_TIMEOUT = 20.0
|
||||
|
||||
def bAreWeConnected():
|
||||
# FixMe: Linux
|
||||
def bAreWeConnected() -> bool:
|
||||
# FixMe: Linux only
|
||||
sFile = f"/proc/{os.getpid()}/net/route"
|
||||
if not os.path.isfile(sFile): return None
|
||||
i = 0
|
||||
@ -33,7 +33,7 @@ def bAreWeConnected():
|
||||
i += 1
|
||||
return i > 0
|
||||
|
||||
def pick_up_proxy_from_environ():
|
||||
def pick_up_proxy_from_environ() -> dict:
|
||||
retval = dict()
|
||||
if os.environ.get('socks_proxy', ''):
|
||||
# socks_proxy takes precedence over https/http
|
||||
@ -68,14 +68,14 @@ def pick_up_proxy_from_environ():
|
||||
retval['udp_enabled'] = True
|
||||
return retval
|
||||
|
||||
def download_url(url, settings=None):
|
||||
def download_url(url:str, settings:str = None) -> None:
|
||||
if not bAreWeConnected(): return ''
|
||||
|
||||
|
||||
if settings is None:
|
||||
settings = pick_up_proxy_from_environ()
|
||||
|
||||
|
||||
if pycurl:
|
||||
LOG.debug('nodes loading with pycurl: ' + str(url))
|
||||
LOG.debug('Downloading with pycurl: ' + str(url))
|
||||
buffer = BytesIO()
|
||||
c = pycurl.Curl()
|
||||
c.setopt(c.URL, url)
|
||||
@ -114,12 +114,12 @@ def download_url(url, settings=None):
|
||||
LOG.info('nodes loaded with pycurl: ' + str(url))
|
||||
return result
|
||||
except Exception as ex:
|
||||
LOG.error('TOX nodes loading error with pycurl: ' + str(ex))
|
||||
LOG.error('TOX Downloading error with pycurl: ' + str(ex))
|
||||
LOG.error('\n' + traceback.format_exc())
|
||||
# drop through
|
||||
|
||||
if requests:
|
||||
LOG.debug('nodes loading with requests: ' + str(url))
|
||||
LOG.debug('Downloading with requests: ' + str(url))
|
||||
try:
|
||||
headers = dict()
|
||||
headers['Content-Type'] = 'application/json'
|
||||
@ -144,11 +144,11 @@ def download_url(url, settings=None):
|
||||
LOG.info('nodes loaded with requests: ' + str(url))
|
||||
return result
|
||||
except Exception as ex:
|
||||
LOG.error('TOX nodes loading error with requests: ' + str(ex))
|
||||
LOG.error('TOX Downloading error with requests: ' + str(ex))
|
||||
# drop through
|
||||
|
||||
if not settings['proxy_type']: # no proxy
|
||||
LOG.debug('nodes loading with urllib no proxy: ' + str(url))
|
||||
LOG.debug('Downloading with urllib no proxy: ' + str(url))
|
||||
try:
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
@ -157,8 +157,7 @@ def download_url(url, settings=None):
|
||||
LOG.info('nodes loaded with no proxy: ' + str(url))
|
||||
return result
|
||||
except Exception as ex:
|
||||
LOG.error('TOX nodes loading ' + str(ex))
|
||||
LOG.error('TOX Downloading ' + str(ex))
|
||||
return ''
|
||||
|
||||
return ''
|
||||
|
584
src/toxygen_wrapper/tests/support_onions.py
Normal file
584
src/toxygen_wrapper/tests/support_onions.py
Normal file
@ -0,0 +1,584 @@
|
||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||
"""
|
||||
Code to interact with a running to using the stem library.
|
||||
"""
|
||||
|
||||
import getpass
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import shutil
|
||||
import socket
|
||||
import sys
|
||||
import logging
|
||||
import time
|
||||
from typing import Union, Callable, Optional
|
||||
import warnings
|
||||
|
||||
import stem
|
||||
from stem.connection import MissingPassword
|
||||
from stem.control import Controller
|
||||
from stem.util.tor_tools import is_valid_fingerprint
|
||||
|
||||
global LOG
|
||||
from toxygen_wrapper.tests.support_http import bAreWeConnected
|
||||
|
||||
warnings.filterwarnings('ignore')
|
||||
LOG = logging.getLogger('TestS')
|
||||
|
||||
bHAVE_TORR = shutil.which('tor-resolve')
|
||||
oSTEM_CONTROLER = None
|
||||
yKNOWN_ONIONS = """
|
||||
- facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd # facebook
|
||||
- duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad # ddg
|
||||
- zkaan2xfbuxia2wpf7ofnkbz6r5zdbbvxbunvp5g2iebopbfc4iqmbad # keys.openpgp
|
||||
"""
|
||||
# grep -B 1 '<li><a href="' /tmp/tor.html |sed -e 's/<li><a href="http:../ - /' -e 's/.onion.*//' -e 's/<li id=./ # /' -e 's/".*//' -e '/^--/d' -e '/<li id/d'
|
||||
# This will slow things down 1-2 min each
|
||||
yKNOWN_ONIONS_TOR = """
|
||||
# 2019.www.torproject.org
|
||||
- jqyzxhjk6psc6ul5jnfwloamhtyh7si74b4743k2qgpskwwxrzhsxmad
|
||||
# api.donate.torproject.org
|
||||
- rbi3fpvpz4vlrx67scoqef2zxz7k4xyiludszg655favvkygjmhz6wyd
|
||||
# archive.torproject.org
|
||||
- uy3qxvwzwoeztnellvvhxh7ju7kfvlsauka7avilcjg7domzxptbq7qd
|
||||
# aus1.torproject.org
|
||||
- ot3ivcdxmalbsbponeeq5222hftpf3pqil24q3s5ejwo5t52l65qusid
|
||||
# aus2.torproject.org
|
||||
- b5t7emfr2rn3ixr4lvizpi3stnni4j4p6goxho7lldf4qg4hz5hvpqid
|
||||
# blog.torproject.org
|
||||
- pzhdfe7jraknpj2qgu5cz2u3i4deuyfwmonvzu5i3nyw4t4bmg7o5pad
|
||||
# bridges.torproject.org
|
||||
- yq5jjvr7drkjrelzhut7kgclfuro65jjlivyzfmxiq2kyv5lickrl4qd
|
||||
# cloud.torproject.org
|
||||
- ui3cpcohcoko6aydhuhlkwqqtvadhaflcc5zb7mwandqmcal7sbwzwqd
|
||||
# collector.torproject.org
|
||||
- pgmrispjerzzf2tdzbfp624cg5vpbvdw2q5a3hvtsbsx25vnni767yad
|
||||
# collector2.torproject.org
|
||||
- 3srlmjzbyyzz62jvdfqwn5ldqmh6mwnqxre2zamxveb75uz2qrqkrkyd
|
||||
# community.torproject.org
|
||||
- xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd
|
||||
# consensus-health.torproject.org
|
||||
- tkskz5dkjel4xqyw5d5l3k52kgglotwn6vgb5wrl2oa5yi2szvywiyid
|
||||
# crm.torproject.org
|
||||
- 6ojylpznauimd2fga3m7g24vd7ebkzlemxdprxckevqpzs347ugmynqd
|
||||
# deb.torproject.org
|
||||
- apow7mjfryruh65chtdydfmqfpj5btws7nbocgtaovhvezgccyjazpqd
|
||||
# dev.crm.torproject.org
|
||||
- eewp4iydzyu2a5d6bvqadadkozxdbhsdtmsoczu2joexfrjjsheaecad
|
||||
# dist.torproject.org
|
||||
- scpalcwstkydpa3y7dbpkjs2dtr7zvtvdbyj3dqwkucfrwyixcl5ptqd
|
||||
# donate-api.torproject.org
|
||||
- lkfkuhcx62yfvzuz5o3ju4divuf4bsh2bybwd3oierq47kyp2ig2gvid
|
||||
# donate.torproject.org
|
||||
- yoaenchicimox2qdc47p36zm3cuclq7s7qxx6kvxqaxjodigfifljqqd
|
||||
# exonerator.torproject.org
|
||||
- pm46i5h2lfewyx6l7pnicbxhts2sxzacvsbmqiemqaspredf2gm3dpad
|
||||
# extra.torproject.org
|
||||
- kkr72iohlfix5ipjg776eyhplnl2oiv5tz4h2y2bkhjix3quafvjd5ad
|
||||
# gettor.torproject.org
|
||||
- ueghr2hzndecdntou33mhymbbxj7pir74nwzhqr6drhxpbz3j272p4id
|
||||
# git.torproject.org
|
||||
- xtlfhaspqtkeeqxk6umggfbr3gyfznvf4jhrge2fujz53433i2fcs3id
|
||||
# gitlab.torproject.org
|
||||
- eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad
|
||||
# gitweb.torproject.org
|
||||
- gzgme7ov25seqjbphab4fkcph3jkobfwwpivt5kzbv3kqx2y2qttl4yd
|
||||
# grafana1.torproject.org
|
||||
- 7zjnw5lx2x27rwiocxkqdquo7fawj46mf2wiu2l7e6z6ng6nivmdxnad
|
||||
# grafana2.torproject.org
|
||||
- f3vd6fyiccuppybkxiblgigej3pfvvqzjnhd3wyv7h4ee5asawf2fhqd
|
||||
# ircbouncer.torproject.org
|
||||
- moz5kotsnjony4oxccxfo4lwk3pvoxmdoljibhgoonzgzjs5oemtjmqd
|
||||
# metabase.metrics.torproject.org
|
||||
- gr5pseamigereei4c6654hafzhid5z2c3oqzn6cfnx7yfyelt47znhad
|
||||
# metrics.torproject.org
|
||||
- hctxrvjzfpvmzh2jllqhgvvkoepxb4kfzdjm6h7egcwlumggtktiftid
|
||||
# moat.torproject.org
|
||||
- z7m7ogzdhu43nosvjtsuplfmuqa3ge5obahixydhmzdox6owwxfoxzid
|
||||
# nagios.torproject.org
|
||||
- w6vizvw4ckesva5fvlkrepynemxdq6pgo5sh4r76ec6msq5notkhqryd
|
||||
# newsletter.torproject.org
|
||||
- a4ygisnerpgtc5ayerl22pll6cls3oyj54qgpm7qrmb66xrxts6y3lyd
|
||||
# nightlies.tbb.torproject.org
|
||||
- umj4zbqdfcyevlkgqgpq6foxk3z75zzxsbgt5jqmfxofrbrjh3crbnad
|
||||
# nyx.torproject.org
|
||||
- 3ewfgrt4gzfccp6bnquhqb266r3zepiqpnsk3falwygkegtluwuyevid
|
||||
- xao2lxsmia2edq2n5zxg6uahx6xox2t7bfjw6b5vdzsxi7ezmqob6qid
|
||||
- dud2sxm6feahhuwj4y4lzktduy7v3qpaqsfkggtj2ojmzathttkegoid
|
||||
# openpgpkey.torproject.org
|
||||
- 2yldcptk56shc7lwieozoglw3t5ghty7m6mf2faysvfnzccqavbu2mad
|
||||
# people.torproject.org
|
||||
- 5ecey6oe4rocdsfoigr4idu42cecm2j7zfogc3xc7kfn4uriehwrs6qd
|
||||
# prometheus1.torproject.org
|
||||
- ydok5jiruh3ak6hcfdlm2g7iuraaxcomeckj2nucjsxif6qmrrda2byd
|
||||
# prometheus2.torproject.org
|
||||
- vyo6yrqhl3by7d6n5t6hjkflaqbarjpqjnvapr5u5rafk4imnfrmcjyd
|
||||
# rbm.torproject.org
|
||||
- nkuz2tpok7ctwd5ueer5bytj3bm42vp7lgjcsnznal3stotg6vyaakyd
|
||||
# research.torproject.org
|
||||
- xhqthou6scpfnwjyzc3ekdgcbvj76ccgyjyxp6cgypxjlcuhnxiktnqd
|
||||
# review.torproject.net
|
||||
- zhkhhhnppc5k6xju7n25rjba3wuip73jnodicxl65qdpchrwvvsilcyd
|
||||
# rpm.torproject.org
|
||||
- 4ayyzfoh5qdrokqaejis3rdredhvf22n3migyxfudpkpunngfc7g4lqd
|
||||
# snowflake.torproject.org
|
||||
- oljlphash3bpqtrvqpr5gwzrhroziw4mddidi5d2qa4qjejcbrmoypqd
|
||||
# spec.torproject.org
|
||||
- i3xi5qxvbrngh3g6o7czwjfxwjzigook7zxzjmgwg5b7xnjcn5hzciad
|
||||
# staging-api.donate.torproject.org
|
||||
- vorwws6g6mx23djlznmlqva4t5olulpnet6fxyiyytcu5dorp3fstdqd
|
||||
# staging.crm.torproject.org
|
||||
- pt34uujusar4arrvsqljndqlt7tck2d5cosaav5xni4nh7bmvshyp2yd
|
||||
# staging.donate-api.torproject.org
|
||||
- 7niqsyixinnhxvh33zh5dqnplxnc2yd6ktvats3zmtbbpzcphpbsa6qd
|
||||
# status.torproject.org
|
||||
- eixoaclv7qvnmu5rolbdwba65xpdiditdoyp6edsre3fitad777jr3ad
|
||||
# stem.torproject.org
|
||||
- mf34jlghauz5pxjcmdymdqbe5pva4v24logeys446tdrgd5lpsrocmqd
|
||||
# styleguide.torproject.org
|
||||
- 7khzpw47s35pwo3lvtctwf2szvnq3kgglvzc22elx7of2awdzpovqmqd
|
||||
# submission.torproject.org
|
||||
- givpjczyrb5jjseful3o5tn3tg7tidbu4gydl4sa5ekpcipivqaqnpad
|
||||
# support.torproject.org
|
||||
- rzuwtpc4wb3xdzrj3yeajsvm3fkq4vbeubm2tdxaqruzzzgs5dwemlad
|
||||
# survey.torproject.org
|
||||
- eh5esdnd6fkbkapfc6nuyvkjgbtnzq2is72lmpwbdbxepd2z7zbgzsqd
|
||||
# svn-archive.torproject.org
|
||||
- b63iq6es4biaawfilwftlfkw6a6putogxh4iakei2ioppb7dsfucekyd
|
||||
# tb-manual.torproject.org
|
||||
- dsbqrprgkqqifztta6h3w7i2htjhnq7d3qkh3c7gvc35e66rrcv66did
|
||||
# test-api.donate.torproject.org
|
||||
- wiofesr5qt2k7qrlljpk53isgedxi6ddw6z3o7iay2l7ne3ziyagxaid
|
||||
# test-data.tbb.torproject.org
|
||||
- umbk3kbgov4ekg264yulvbrpykfye7ohguqbds53qn547mdpt6o4qkad
|
||||
# test.crm.torproject.org
|
||||
- a4d52y2erv4eijii66cpnyqn7rsnnq3gmtrsdxzt2laoutvu4gz7fwid
|
||||
# test.donate-api.torproject.org
|
||||
- i4zhrn4md3ucd5dfgeo5lnqd3jy2z2kzp3lt4tdisvivzoqqtlrymkid
|
||||
# www
|
||||
- tttyx2vwp7ihml3vkhywwcizv6nbwrikpgeciy3qrow7l7muak2pnhad
|
||||
# www.torproject.org
|
||||
- 2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid
|
||||
"""
|
||||
|
||||
# we check these each time but we got them by sorting bad relays
|
||||
# in the wild we'll keep a copy here so we can avoid restesting
|
||||
yKNOWN_NODNS = """
|
||||
- 0x0.is
|
||||
- a9.wtf
|
||||
- apt96.com
|
||||
- axims.net
|
||||
- backup.spekadyon.org
|
||||
- dfri.se
|
||||
- dotsrc.org
|
||||
- dtf.contact
|
||||
- ezyn.de
|
||||
- for-privacy.net
|
||||
- galtland.network
|
||||
- heraldonion.org
|
||||
- interfesse.net
|
||||
- kryptonit.org
|
||||
- linkspartei.org
|
||||
- mkg20001.io
|
||||
- nicdex.com
|
||||
- nx42.de
|
||||
- pineapple.cx
|
||||
- privacylayer.xyz
|
||||
- privacysvcs.net
|
||||
- prsv.ch
|
||||
- sebastian-elisa-pfeifer.eu
|
||||
- thingtohide.nl
|
||||
- tor-exit-2.aa78i2efsewr0neeknk.xyz
|
||||
- tor-exit-3.aa78i2efsewr0neeknk.xyz
|
||||
- tor.dlecan.com
|
||||
- tor.skankhunt42.pw
|
||||
- transliberation.today
|
||||
- tuxli.org
|
||||
- unzane.com
|
||||
- verification-for-nusenu.net
|
||||
- www.defcon.org
|
||||
"""
|
||||
# - aklad5.com
|
||||
# - artikel5ev.de
|
||||
# - arvanode.net
|
||||
# - dodo.pm
|
||||
# - erjan.net
|
||||
# - galtland.network
|
||||
# - lonet.sh
|
||||
# - moneneis.de
|
||||
# - olonet.sh
|
||||
# - or-exit-2.aa78i2efsewr0neeknk.xyz
|
||||
# - or.wowplanet.de
|
||||
# - ormycloud.org
|
||||
# - plied-privacy.net
|
||||
# - rivacysvcs.net
|
||||
# - redacted.org
|
||||
# - rofl.cat
|
||||
# - sv.ch
|
||||
# - tikel10.org
|
||||
# - tor.wowplanet.de
|
||||
# - torix-relays.org
|
||||
# - tse.com
|
||||
# - w.digidow.eu
|
||||
# - w.cccs.de
|
||||
|
||||
def oMakeController(sSock:str = '', port:int = 9051, password:[str,None] = None):
|
||||
global oSTEM_CONTROLER
|
||||
from getpass import unix_getpass
|
||||
if sSock and os.path.exists(sSock):
|
||||
controller = Controller.from_socket_file(path=sSock)
|
||||
else:
|
||||
controller = Controller.from_port(port=port)
|
||||
try:
|
||||
controller.authenticate()
|
||||
except (Exception, MissingPassword):
|
||||
if password is None:
|
||||
password = os.environ.get('TOR_CONTROLLER_PASSWORD', '')
|
||||
if password:
|
||||
p = password
|
||||
else:
|
||||
sys.stdout.flush()
|
||||
p = unix_getpass(prompt='TOR_CONTROLLER_PASSWORD: ', stream=sys.stderr)
|
||||
controller.authenticate(p)
|
||||
oSTEM_CONTROLER = controller
|
||||
return controller
|
||||
|
||||
def oGetStemController(log_level:int = 10, sock_or_pair:str = '/run/tor/control', password:[str,None] = None):
|
||||
|
||||
global oSTEM_CONTROLER
|
||||
if oSTEM_CONTROLER: return oSTEM_CONTROLER
|
||||
import stem.util.log
|
||||
# stem.util.log.Runlevel = 'DEBUG' = 20 # log_level
|
||||
|
||||
if os.path.exists(sock_or_pair):
|
||||
LOG.info(f"controller from socket {sock_or_pair}")
|
||||
controller = Controller.from_socket_file(path=sock_or_pair)
|
||||
else:
|
||||
if type(sock_or_pair) == int:
|
||||
port = sock_or_pair
|
||||
elif ':' in sock_or_pair:
|
||||
port = sock_or_pair.split(':')[1]
|
||||
else:
|
||||
port = sock_or_pair
|
||||
try:
|
||||
port = int(port)
|
||||
except: port = 9051
|
||||
LOG.info(f"controller from port {port}")
|
||||
controller = Controller.from_port(port=port)
|
||||
try:
|
||||
controller.authenticate()
|
||||
except (Exception, MissingPassword):
|
||||
if password is None:
|
||||
password = os.environ.get('TOR_CONTROLLER_PASSWORD', '')
|
||||
if password:
|
||||
p = password
|
||||
else:
|
||||
sys.stdout.flush()
|
||||
p = getpass.unix_getpass(prompt='TOR_CONTROLLER_PASSWORD: ', stream=sys.stderr)
|
||||
controller.authenticate(p)
|
||||
oSTEM_CONTROLER = controller
|
||||
LOG.debug(f"{controller}")
|
||||
return oSTEM_CONTROLER
|
||||
|
||||
def sMapaddressResolv(target:str, iPort:int = 9051, log_level:int = 10, password:[str,None] = None) -> str:
|
||||
if not stem:
|
||||
LOG.warn('please install the stem Python package')
|
||||
return ''
|
||||
|
||||
try:
|
||||
controller = oGetStemController(log_level=log_level, password=password)
|
||||
map_dict = {"0.0.0.0": target}
|
||||
map_ret = controller.map_address(map_dict)
|
||||
return map_ret
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
return ''
|
||||
|
||||
def vwait_for_controller(controller, wait_boot:int = 10) -> None:
|
||||
if bAreWeConnected() is False:
|
||||
raise RuntimeError("we are not connected")
|
||||
percent = i = 0
|
||||
# You can call this while boostrapping
|
||||
while percent < 100 and i < wait_boot:
|
||||
bootstrap_status = controller.get_info("status/bootstrap-phase")
|
||||
progress_percent = re.match('.* PROGRESS=([0-9]+).*', bootstrap_status)
|
||||
percent = int(progress_percent.group(1))
|
||||
LOG.info(f"Bootstrapping {percent}%")
|
||||
time.sleep(5)
|
||||
i += 5
|
||||
|
||||
def bin_to_hex(raw_id:int, length: Optional[int] = None) -> str:
|
||||
if length is None: length = len(raw_id)
|
||||
res = ''.join('{:02x}'.format(raw_id[i]) for i in range(length))
|
||||
return res.upper()
|
||||
|
||||
def lIntroductionPoints(controller=None, lOnions:[list,None] = None, itimeout:int = 120, log_level:int = 10, password:[str,None] = None):
|
||||
"""now working !!! stem 1.8.x timeout must be huge >120
|
||||
'Provides the descriptor for a hidden service. The **address** is the
|
||||
'.onion' address of the hidden service '
|
||||
What about Services?
|
||||
"""
|
||||
if lOnions is None: lOnions = []
|
||||
try:
|
||||
from cryptography.utils import int_from_bytes
|
||||
except ImportError:
|
||||
import cryptography.utils
|
||||
# guessing - not in the current cryptography but stem expects it
|
||||
def int_from_bytes(**args): return int.to_bytes(*args)
|
||||
cryptography.utils.int_from_bytes = int_from_bytes
|
||||
# this will fai if the trick above didnt work
|
||||
from stem.prereq import is_crypto_available
|
||||
is_crypto_available(ed25519=True)
|
||||
|
||||
from queue import Empty
|
||||
|
||||
from stem import Timeout
|
||||
from stem.client.datatype import LinkByFingerprint
|
||||
from stem.descriptor.hidden_service import HiddenServiceDescriptorV3
|
||||
|
||||
if type(lOnions) not in [set, tuple, list]:
|
||||
lOnions = list(lOnions)
|
||||
if controller is None:
|
||||
controller = oGetStemController(log_level=log_level, password=password)
|
||||
l = []
|
||||
for elt in lOnions:
|
||||
LOG.info(f"controller.get_hidden_service_descriptor {elt}")
|
||||
try:
|
||||
desc = controller.get_hidden_service_descriptor(elt,
|
||||
await_result=True,
|
||||
timeout=itimeout)
|
||||
# LOG.log(40, f"{dir(desc)} get_hidden_service_descriptor")
|
||||
# timeouts 20 sec
|
||||
# mistakenly a HSv2 descriptor
|
||||
hs_address = HiddenServiceDescriptorV3.from_str(str(desc)) # reparse as HSv3
|
||||
oInnerLayer = hs_address.decrypt(elt)
|
||||
# LOG.log(40, f"{dir(oInnerLayer)}")
|
||||
|
||||
# IntroductionPointV3
|
||||
n = oInnerLayer.introduction_points
|
||||
if not n:
|
||||
LOG.warn(f"NO introduction points for {elt}")
|
||||
continue
|
||||
LOG.info(f"{elt} {len(n)} introduction points")
|
||||
lp = []
|
||||
for introduction_point in n:
|
||||
for linkspecifier in introduction_point.link_specifiers:
|
||||
if isinstance(linkspecifier, LinkByFingerprint):
|
||||
# LOG.log(40, f"Getting fingerprint for {linkspecifier}")
|
||||
if hasattr(linkspecifier, 'fingerprint'):
|
||||
assert len(linkspecifier.value) == 20
|
||||
lp += [bin_to_hex(linkspecifier.value)]
|
||||
LOG.info(f"{len(lp)} introduction points for {elt}")
|
||||
l += lp
|
||||
except (Empty, Timeout,) as e: # noqa
|
||||
LOG.warn(f"Timed out getting introduction points for {elt}")
|
||||
except stem.DescriptorUnavailable as e:
|
||||
LOG.error(e)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
return l
|
||||
|
||||
def zResolveDomain(domain:str) -> int:
|
||||
try:
|
||||
ip = sTorResolve(domain)
|
||||
except Exception as e: # noqa
|
||||
ip = ''
|
||||
if ip == '':
|
||||
try:
|
||||
lpair = getaddrinfo(domain, 443)
|
||||
except Exception as e:
|
||||
LOG.warn(f"{e}")
|
||||
lpair = None
|
||||
if lpair is None:
|
||||
LOG.warn(f"TorResolv and getaddrinfo failed for {domain}")
|
||||
return ''
|
||||
ip = lpair[0]
|
||||
return ip
|
||||
|
||||
def sTorResolve(target:str,
|
||||
verbose:bool = False,
|
||||
sHost:str = '127.0.0.1',
|
||||
iPort:int = 9050,
|
||||
SOCK_TIMEOUT_SECONDS:float = 10.0,
|
||||
SOCK_TIMEOUT_TRIES:int = 3,
|
||||
) -> str:
|
||||
MAX_INFO_RESPONSE_PACKET_LENGTH = 8
|
||||
if '@' in target:
|
||||
LOG.warn(f"sTorResolve failed invalid hostname {target}")
|
||||
return ''
|
||||
target = target.strip('/')
|
||||
seb = b"\x04\xf0\x00\x00\x00\x00\x00\x01\x00"
|
||||
seb += bytes(target, 'US-ASCII') + b"\x00"
|
||||
assert len(seb) == 10 + len(target), str(len(seb)) + repr(seb)
|
||||
|
||||
# LOG.debug(f"0 Sending {len(seb)} to The TOR proxy {seb}")
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((sHost, iPort))
|
||||
|
||||
sock.settimeout(SOCK_TIMEOUT_SECONDS)
|
||||
oRet = sock.sendall(seb) # noqa
|
||||
|
||||
i = 0
|
||||
data = ''
|
||||
while i < SOCK_TIMEOUT_TRIES:
|
||||
i += 1
|
||||
time.sleep(3)
|
||||
lReady = select.select([sock.fileno()], [], [],
|
||||
SOCK_TIMEOUT_SECONDS)
|
||||
if not lReady[0]: continue
|
||||
try:
|
||||
flags = socket.MSG_WAITALL
|
||||
data = sock.recv(MAX_INFO_RESPONSE_PACKET_LENGTH, flags)
|
||||
except socket.timeout:
|
||||
LOG.warn(f"4 The TOR proxy {(sHost, iPort)}" \
|
||||
+" didnt reply in " + str(SOCK_TIMEOUT_SECONDS) + " sec."
|
||||
+" #" +str(i))
|
||||
except Exception as e:
|
||||
LOG.error("4 The TOR proxy " \
|
||||
+repr((sHost, iPort)) \
|
||||
+" errored with " + str(e)
|
||||
+" #" +str(i))
|
||||
sock.close()
|
||||
return ''
|
||||
else:
|
||||
if len(data) > 0: break
|
||||
|
||||
if len(data) == 0:
|
||||
if i > SOCK_TIMEOUT_TRIES:
|
||||
sLabel = "5 No reply #"
|
||||
else:
|
||||
sLabel = "5 No data #"
|
||||
LOG.warn(f"sTorResolve: {sLabel} {i} on {sHost}:{iPort}")
|
||||
sock.close()
|
||||
return ''
|
||||
|
||||
assert len(data) >= 8
|
||||
packet_sf = data[1]
|
||||
if packet_sf == 90:
|
||||
# , "%d" % packet_sf
|
||||
assert f"{packet_sf}" == "90", f"packet_sf = {packet_sf}"
|
||||
return f"{data[4]}.{data[5]}.{data[6]}.{data[7]}"
|
||||
else:
|
||||
# 91
|
||||
LOG.warn(f"tor-resolve failed for {target} on {sHost}:{iPort}")
|
||||
sout = f"/tmp/{os.getpid}.tmp"
|
||||
iRet = os.system(f"tor-resolve -4 {target} > {sout} 2>/dev/null")
|
||||
# os.system("strace tor-resolve -4 "+target+" 2>&1|grep '^sen\|^rec'")
|
||||
if iRet == 0:
|
||||
sAns = open(sout, 'rt').read().strip()
|
||||
return sAns
|
||||
return ''
|
||||
|
||||
def getaddrinfo(sHost:str, sPort:str) -> list:
|
||||
# do this the explicit way = Ive seen the compact connect fail
|
||||
# >>> sHost, sPort = 'l27.0.0.1', 33446
|
||||
# >>> sock.connect((sHost, sPort))
|
||||
# socket.gaierror: [Errno -2] Name or service not known
|
||||
try:
|
||||
lElts = socket.getaddrinfo(sHost, int(sPort), socket.AF_INET)
|
||||
lElts = list(filter(lambda elt: elt[1] == socket.SOCK_DGRAM, lElts))
|
||||
assert len(lElts) == 1, repr(lElts)
|
||||
lPair = lElts[0][-1]
|
||||
assert len(lPair) == 2, repr(lPair)
|
||||
assert type(lPair[1]) == int, repr(lPair)
|
||||
except (socket.gaierror, OSError, BaseException) as e:
|
||||
LOG.error(e)
|
||||
return None
|
||||
return lPair
|
||||
|
||||
def icheck_torrc(sFile:str, oArgs) -> int:
|
||||
l = open(sFile, 'rt').readlines()
|
||||
a = {}
|
||||
for elt in l:
|
||||
elt = elt.strip()
|
||||
if not elt or ' ' not in elt: continue
|
||||
(k, v,) = elt.split(' ', 1)
|
||||
a[k] = v
|
||||
keys = a
|
||||
|
||||
if 'HashedControlPassword' not in keys:
|
||||
LOG.info('Add HashedControlPassword for security')
|
||||
print('run: tor --hashcontrolpassword <TopSecretWord>')
|
||||
if 'ExcludeExitNodes' in keys:
|
||||
elt = 'BadNodes.ExcludeExitNodes.BadExit'
|
||||
LOG.warn(f"Remove ExcludeNodes and move then to {oArgs.bad_nodes}")
|
||||
print(f"move to the {elt} section as a list")
|
||||
if 'GuardNodes' in keys:
|
||||
elt = 'GoodNodes.GuardNodes'
|
||||
LOG.warn(f"Remove GuardNodes and move then to {oArgs.good_nodes}")
|
||||
print(f"move to the {elt} section as a list")
|
||||
if 'ExcludeNodes' in keys:
|
||||
elt = 'BadNodes.ExcludeNodes.BadExit'
|
||||
LOG.warn(f"Remove ExcludeNodes and move then to {oArgs.bad_nodes}")
|
||||
print(f"move to the {elt} section as a list")
|
||||
if 'ControlSocket' not in keys and os.path.exists('/run/tor/control'):
|
||||
LOG.info('Add ControlSocket /run/tor/control for us')
|
||||
print('ControlSocket /run/tor/control GroupWritable RelaxDirModeCheck')
|
||||
if 'UseMicrodescriptors' not in keys or keys['UseMicrodescriptors'] != '1':
|
||||
LOG.info('Add UseMicrodescriptors 0 for us')
|
||||
print('UseMicrodescriptors 0')
|
||||
if 'AutomapHostsSuffixes' not in keys:
|
||||
LOG.info('Add AutomapHostsSuffixes for onions')
|
||||
print('AutomapHostsSuffixes .exit,.onion')
|
||||
if 'AutoMapHostsOnResolve' not in keys:
|
||||
LOG.info('Add AutoMapHostsOnResolve for onions')
|
||||
print('AutoMapHostsOnResolve 1')
|
||||
if 'VirtualAddrNetworkIPv4' not in keys:
|
||||
LOG.info('Add VirtualAddrNetworkIPv4 for onions')
|
||||
print('VirtualAddrNetworkIPv4 172.16.0.0/12')
|
||||
return 0
|
||||
|
||||
def lExitExcluder(oArgs, iPort:int = 9051, log_level:int = 10, password:[str,None] = None) -> list:
|
||||
"""
|
||||
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=log_level, password=password)
|
||||
# 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
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) <= 1:
|
||||
targets = ['zkaan2xfbuxia2wpf7ofnkbz6r5zdbbvxbunvp5g2iebopbfc4iqmbad', # keys.openpgp.org
|
||||
'facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd', # facebook
|
||||
'libera75jm6of4wxpxt4aynol3xjmbtxgfyjpu34ss4d7r7q2v5zrpyd', # libera
|
||||
]
|
||||
else:
|
||||
targets = sys.argv[1:]
|
||||
|
||||
controller = oGetStemController(log_level=10)
|
||||
for target in targets:
|
||||
print(target, lIntroductionPoints(controller, [target], itimeout=120))
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1774
src/toxygen_wrapper/tests/tests_wrapper.py
Normal file
1774
src/toxygen_wrapper/tests/tests_wrapper.py
Normal file
File diff suppressed because it is too large
Load Diff
662
src/toxygen_wrapper/tests/wrapper_mixin.py
Normal file
662
src/toxygen_wrapper/tests/wrapper_mixin.py
Normal file
@ -0,0 +1,662 @@
|
||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
from ctypes import *
|
||||
from typing import Union, Callable, Optional
|
||||
|
||||
import toxygen_wrapper.toxcore_enums_and_consts as enums
|
||||
from toxygen_wrapper.tox import Tox, UINT32_MAX, ToxError
|
||||
|
||||
from toxygen_wrapper.toxcore_enums_and_consts import (TOX_ADDRESS_SIZE, TOX_CONNECTION,
|
||||
TOX_FILE_CONTROL,
|
||||
TOX_MESSAGE_TYPE,
|
||||
TOX_SECRET_KEY_SIZE,
|
||||
TOX_USER_STATUS)
|
||||
|
||||
try:
|
||||
import support_testing as ts
|
||||
except ImportError:
|
||||
import toxygen_wrapper.tests.support_testing as ts
|
||||
sleep = time.sleep
|
||||
|
||||
ADDR_SIZE = 38 * 2
|
||||
CLIENT_ID_SIZE = 32 * 2
|
||||
THRESHOLD = 120 # >25
|
||||
fSOCKET_TIMEOUT = 15.0
|
||||
iLOOP_N = 50
|
||||
|
||||
iN = 6
|
||||
|
||||
global LOG
|
||||
LOG = logging.getLogger('TestS')
|
||||
if False:
|
||||
def LOG_ERROR(l: str) -> None: LOG.error('+ '+l)
|
||||
def LOG_WARN(l: str) -> None: LOG.warn('+ '+l)
|
||||
def LOG_INFO(l: str) -> None: LOG.info('+ '+l)
|
||||
def LOG_DEBUG(l: str) -> None: LOG.debug('+ '+l)
|
||||
def LOG_TRACE(l: str) -> None: pass # print('+ '+l)
|
||||
else:
|
||||
# just print to stdout so there is NO complications from logging.
|
||||
def LOG_ERROR(l: str) -> None: print('EROR+ '+l)
|
||||
def LOG_WARN(l: str) -> None: print('WARN+ '+l)
|
||||
def LOG_INFO(l: str) -> None: print('INFO+ '+l)
|
||||
def LOG_DEBUG(l: str) -> None: print('DEBUG+ '+l)
|
||||
def LOG_TRACE(l: str) -> None: pass # print('TRAC+ '+l)
|
||||
|
||||
class WrapperMixin():
|
||||
|
||||
def bBobNeedAlice(self) -> bool:
|
||||
"""
|
||||
"""
|
||||
if hasattr(self, 'baid') and self.baid >= 0 and \
|
||||
self.baid in self.bob.self_get_friend_list():
|
||||
LOG.warn(f"setUp ALICE IS ALREADY IN BOBS FRIEND LIST")
|
||||
return False
|
||||
elif self.bob.self_get_friend_list_size() >= 1:
|
||||
LOG.warn(f"setUp BOB STILL HAS A FRIEND LIST")
|
||||
return False
|
||||
return True
|
||||
|
||||
def bAliceNeedAddBob (self) -> bool:
|
||||
if hasattr(self, 'abid') and self.abid >= 0 and \
|
||||
self.abid in self.alice.self_get_friend_list():
|
||||
LOG.warn(f"setUp BOB IS ALREADY IN ALICES FRIEND LIST")
|
||||
return False
|
||||
if self.alice.self_get_friend_list_size() >= 1:
|
||||
LOG.warn(f"setUp ALICE STILL HAS A FRIEND LIST")
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_connection_status(self) -> bool:
|
||||
if self.bob.mycon_time <= 1 or self.alice.mycon_time <= 1:
|
||||
pass
|
||||
# drop through
|
||||
elif self.bob.dht_connected == TOX_CONNECTION['NONE']:
|
||||
return False
|
||||
elif self.alice.dht_connected == TOX_CONNECTION['NONE']:
|
||||
return False
|
||||
|
||||
# if not self.connected
|
||||
if self.bob.self_get_connection_status() == TOX_CONNECTION['NONE']:
|
||||
return False
|
||||
if self.alice.self_get_connection_status() == TOX_CONNECTION['NONE']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def loop(self, n) -> None:
|
||||
"""
|
||||
t:iterate
|
||||
t:iteration_interval
|
||||
"""
|
||||
interval = self.bob.iteration_interval()
|
||||
for i in range(n):
|
||||
self.alice.iterate()
|
||||
self.bob.iterate()
|
||||
sleep(interval / 1000.0)
|
||||
|
||||
def call_bootstrap(self, num: Optional[int] = None, lToxes:Union[list[int], None] =None, i:int =0, fsocket_timeout:float = fSOCKET_TIMEOUT) -> None:
|
||||
if num == None: num=ts.iNODES
|
||||
if lToxes is None:
|
||||
lToxes = [self.alice, self.bob]
|
||||
# LOG.debug(f"call_bootstrap network={oTOX_OARGS.network}")
|
||||
otox = lToxes[0]
|
||||
if otox._args.network in ['new', 'newlocal', 'localnew']:
|
||||
ts.bootstrap_local(self.lUdp, lToxes)
|
||||
elif not ts.bAreWeConnected():
|
||||
LOG.warning('we are NOT CONNECTED')
|
||||
else:
|
||||
random.shuffle(self.lUdp)
|
||||
if otox._args.proxy_port > 0:
|
||||
lElts = self.lUdp[:1]
|
||||
else:
|
||||
lElts = self.lUdp[:num+i]
|
||||
LOG.debug(f"call_bootstrap ts.bootstrap_udp {len(lElts)}")
|
||||
ts.bootstrap_udp(lElts, lToxes, fsocket_timeout=fsocket_timeout)
|
||||
random.shuffle(self.lTcp)
|
||||
lElts = self.lTcp[:num+i]
|
||||
LOG.debug(f"call_bootstrap ts.bootstrap_tcp {len(lElts)}")
|
||||
ts.bootstrap_tcp(lElts, lToxes, fsocket_timeout=fsocket_timeout)
|
||||
|
||||
def group_until_connected(self, otox, group_number:int, num: Optional[int] = None, iMax:int = THRESHOLD, fsocket_timeout:float = fSOCKET_TIMEOUT) -> bool:
|
||||
"""
|
||||
"""
|
||||
i = 0
|
||||
bRet = None
|
||||
while i <= iMax :
|
||||
i += 1
|
||||
iRet = otox.group_is_connected(group_number)
|
||||
if iRet == True:
|
||||
bRet = True
|
||||
break
|
||||
if i % 5 == 0:
|
||||
j = i//5 + 1
|
||||
self.call_bootstrap(num, lToxes=None, i=j, fsocket_timeout=fsocket_timeout)
|
||||
s = ''
|
||||
if i == 0: s = '\n'
|
||||
LOG.info(s+f"group_until_connected #{i} iRet={iRet}" \
|
||||
+f" BOBS={otox.self_conn_status}" \
|
||||
+f" last={int(otox.mycon_time)}" )
|
||||
self.loop(iLOOP_N)
|
||||
else:
|
||||
bRet = False
|
||||
|
||||
if bRet:
|
||||
LOG.info(f"group_until_connected True i={i} iMax={iMax}" \
|
||||
+f" BOB={otox.self_get_connection_status()}" \
|
||||
+f" last={int(otox.mycon_time)}" )
|
||||
return True
|
||||
else:
|
||||
LOG.warning(f"group_until_connected False i={i}" \
|
||||
+f" iMax={iMax}" \
|
||||
+f" BOB={otox.self_get_connection_status()}" \
|
||||
+f" last={int(otox.mycon_time)}" )
|
||||
return False
|
||||
|
||||
def loop_until_connected(self, otox=None, num: Optional[int] = None, fsocket_timeout:float = fSOCKET_TIMEOUT) -> bool:
|
||||
"""
|
||||
t:on_self_connection_status
|
||||
t:self_get_connection_status
|
||||
"""
|
||||
i = 0
|
||||
num = 4
|
||||
bRet = None
|
||||
if otox is None: otox = self.bob
|
||||
while i <= otox._args.test_timeout :
|
||||
i += 1
|
||||
if (self.alice.self_conn_status and self.bob.self_conn_status):
|
||||
bRet = True
|
||||
break
|
||||
if i % 5 == 0:
|
||||
j = i//5 + 1
|
||||
self.call_bootstrap(num, lToxes=None, i=j, fsocket_timeout=fsocket_timeout)
|
||||
s = ''
|
||||
if i == 0: s = '\n'
|
||||
LOG.info(s+"loop_until_connected " \
|
||||
+" #" + str(i) \
|
||||
+" BOB=" +repr(self.bob.self_get_connection_status()) \
|
||||
+" ALICE=" +repr(self.alice.self_get_connection_status())
|
||||
+f" BOBS={self.bob.self_conn_status}" \
|
||||
+f" ALICES={self.alice.self_conn_status}" \
|
||||
+f" last={int(self.bob.mycon_time)}" )
|
||||
if (self.alice.self_conn_status and self.bob.self_conn_status):
|
||||
bRet = True
|
||||
break
|
||||
if (self.alice.self_get_connection_status() and
|
||||
self.bob.self_get_connection_status()):
|
||||
LOG_WARN(f"loop_until_connected disagree status() DISAGREE" \
|
||||
+f' self.bob.self_conn_status={self.bob.self_conn_status}' \
|
||||
+f' alice.self_conn_status={self.alice.self_conn_status}' \
|
||||
+f" last={int(self.bob.mycon_time)}" )
|
||||
bRet = True
|
||||
break
|
||||
self.loop(iLOOP_N)
|
||||
else:
|
||||
bRet = False
|
||||
|
||||
if bRet or \
|
||||
( self.bob.self_get_connection_status() != TOX_CONNECTION['NONE'] and \
|
||||
self.alice.self_get_connection_status() != TOX_CONNECTION['NONE'] ):
|
||||
LOG.info(f"loop_until_connected returning True i={i}" \
|
||||
+f" BOB={self.bob.self_get_connection_status()}" \
|
||||
+f" ALICE={self.alice.self_get_connection_status()}" \
|
||||
+f" last={int(self.bob.mycon_time)}" )
|
||||
return True
|
||||
|
||||
otox._args.test_timeout += 5
|
||||
LOG.warning(f"loop_until_connected returning False i={i}" \
|
||||
+f" BOB={self.bob.self_get_connection_status()}" \
|
||||
+f" ALICE={self.alice.self_get_connection_status()}" \
|
||||
+f" last={int(self.bob.mycon_time)}" )
|
||||
return False
|
||||
|
||||
def wait_objs_attr(self, objs: list, attr: str, fsocket_timeout:float = fSOCKET_TIMEOUT) -> bool:
|
||||
i = 0
|
||||
otox = objs[0]
|
||||
while i <= otox._args.test_timeout:
|
||||
i += 1
|
||||
if i % 5 == 0:
|
||||
num = None
|
||||
j = 0
|
||||
j = i//5
|
||||
self.call_bootstrap(num, lToxes=objs, i=j, fsocket_timeout=fsocket_timeout)
|
||||
LOG.debug(f"wait_objs_attr {objs} for {attr} {i}")
|
||||
if all([getattr(obj, attr) for obj in objs]):
|
||||
return True
|
||||
self.loop(iLOOP_N)
|
||||
else:
|
||||
otox._args.test_timeout += 1
|
||||
LOG.warn(f"wait_objs_attr for {attr} i >= {otox._args.test_timeout}")
|
||||
|
||||
return all([getattr(obj, attr) is not None for obj in objs])
|
||||
|
||||
def wait_otox_attrs(self, obj, attrs: list[str], fsocket_timeout:float = fSOCKET_TIMEOUT) -> bool:
|
||||
assert all(attrs), f"wait_otox_attrs {attrs}"
|
||||
i = 0
|
||||
otox = obj
|
||||
while i <= otox._args.test_timeout:
|
||||
i += 1
|
||||
if i % 5 == 0:
|
||||
num = None
|
||||
j = 0
|
||||
if obj.mycon_time == 1:
|
||||
# start with 4 random nodes ti bootstrap
|
||||
num = 4
|
||||
# every 10 sec add another random nodes to bootstrap
|
||||
j = i//10 + 1
|
||||
if obj.self_get_connection_status() == TOX_CONNECTION['NONE']:
|
||||
self.call_bootstrap(num, lToxes=[obj], i=j, fsocket_timeout=fsocket_timeout)
|
||||
LOG.debug(f"wait_otox_attrs {obj.name} for {attrs} {i}" \
|
||||
+f" last={int(obj.mycon_time)}")
|
||||
if all([getattr(obj, attr) is not None for attr in attrs]):
|
||||
return True
|
||||
self.loop(iLOOP_N)
|
||||
else:
|
||||
LOG.warning(f"wait_otox_attrs i >= {otox._args.test_timeout} attrs={attrs} results={[getattr(obj, attr) for attr in attrs]}")
|
||||
|
||||
return all([getattr(obj, attr) for attr in attrs])
|
||||
|
||||
def wait_ensure_exec(self, method, args:list, fsocket_timeout:float = fSOCKET_TIMEOUT) -> bool:
|
||||
i = 0
|
||||
oRet = None
|
||||
while i <= self.bob._args.test_timeout:
|
||||
i += 1
|
||||
if i % 5 == 0:
|
||||
# every 10 sec add another random nodes to bootstrap
|
||||
j = i//10 + 1
|
||||
self.call_bootstrap(num=None, lToxes=None, i=j, fsocket_timeout=fsocket_timeout)
|
||||
LOG.debug("wait_ensure_exec " \
|
||||
+" " +str(method)
|
||||
+" " +str(i))
|
||||
try:
|
||||
oRet = method(*args)
|
||||
if oRet:
|
||||
LOG.info(f"wait_ensure_exec oRet {oRet!r}")
|
||||
return True
|
||||
except ArgumentError as e:
|
||||
# ArgumentError('This client is currently NOT CONNECTED to the friend.')
|
||||
# dunno
|
||||
LOG.warning(f"wait_ensure_exec ArgumentError {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
LOG.warning(f"wait_ensure_exec EXCEPTION {e}")
|
||||
return False
|
||||
sleep(3)
|
||||
else:
|
||||
LOG.error(f"wait_ensure_exec i >= {1*self.bob._args.test_timeout}")
|
||||
return False
|
||||
|
||||
return oRet
|
||||
|
||||
def bob_add_alice_as_friend_norequest(self) -> bool:
|
||||
if not self.bBobNeedAlice(): return True
|
||||
|
||||
apk = self.alice.self_get_public_key()
|
||||
iRet = self.bob.friend_add_norequest(apk)
|
||||
if iRet < 0:
|
||||
return False
|
||||
self.baid = self.bob.friend_by_public_key(apk)
|
||||
assert self.baid >= 0, self.baid
|
||||
assert self.bob.friend_exists(self.baid), "bob.friend_exists"
|
||||
assert not self.bob.friend_exists(self.baid + 1)
|
||||
assert self.baid in self.bob.self_get_friend_list()
|
||||
assert self.bob.self_get_friend_list_size() >= 1
|
||||
return True
|
||||
|
||||
def alice_add_bob_as_friend_norequest(self) -> bool:
|
||||
if not self.bAliceNeedAddBob(): return True
|
||||
|
||||
bpk = self.bob.self_get_public_key()
|
||||
iRet = self.alice.friend_add_norequest(bpk)
|
||||
if iRet < 0:
|
||||
return False
|
||||
self.abid = self.alice.friend_by_public_key(bpk)
|
||||
assert self.abid >= 0, self.abid
|
||||
assert self.abid in self.alice.self_get_friend_list()
|
||||
assert self.alice.friend_exists(self.abid), "alice.friend_exists"
|
||||
assert not self.alice.friend_exists(self.abid + 1)
|
||||
assert self.alice.self_get_friend_list_size() >= 1
|
||||
return True
|
||||
|
||||
def both_add_as_friend(self) -> bool:
|
||||
if self.bob._args.norequest:
|
||||
assert self.bob_add_alice_as_friend_norequest()
|
||||
assert self.alice_add_bob_as_friend_norequest()
|
||||
else:
|
||||
assert self.bob_add_alice_as_friend()
|
||||
assert self.alice_add_bob_as_friend()
|
||||
if not hasattr(self, 'baid') or self.baid < 0:
|
||||
LOG.warn("both_add_as_friend no bob, baid")
|
||||
if not hasattr(self, 'abid') or self.abid < 0:
|
||||
LOG.warn("both_add_as_friend no alice, abid")
|
||||
return True
|
||||
|
||||
def both_add_as_friend_norequest(self) -> bool:
|
||||
if self.bBobNeedAlice():
|
||||
assert self.bob_add_alice_as_friend_norequest()
|
||||
if self.bAliceNeedAddBob():
|
||||
assert self.alice_add_bob_as_friend_norequest()
|
||||
if not hasattr(self.bob, 'baid') or self.bob.baid < 0:
|
||||
LOG.warn("both_add_as_friend_norequest no bob, baid")
|
||||
if not hasattr(self.alice, 'abid') or self.alice.abid < 0:
|
||||
LOG.warn("both_add_as_friend_norequest no alice, abid")
|
||||
|
||||
#: Test last online
|
||||
#? assert self.alice.friend_get_last_online(self.abid) is not None
|
||||
#? assert self.bob.friend_get_last_online(self.baid) is not None
|
||||
return True
|
||||
|
||||
def bob_add_alice_as_friend(self) -> bool:
|
||||
"""
|
||||
t:friend_add
|
||||
t:on_friend_request
|
||||
t:friend_by_public_key
|
||||
"""
|
||||
MSG = 'Alice, this is Bob.'
|
||||
sSlot = 'friend_request'
|
||||
if not self.bBobNeedAlice(): return True
|
||||
|
||||
def alices_on_friend_request(iTox,
|
||||
public_key,
|
||||
message_data,
|
||||
message_data_size,
|
||||
*largs) -> None:
|
||||
try:
|
||||
assert str(message_data, 'UTF-8') == MSG
|
||||
LOG_INFO(f"alices_on_friend_request: {sSlot} = True ")
|
||||
except Exception as e:
|
||||
LOG_WARN(f"alices_on_friend_request: EXCEPTION {e}")
|
||||
# return
|
||||
setattr(self.bob, sSlot, True)
|
||||
|
||||
setattr(self.bob, sSlot, None)
|
||||
apk = self.alice.self_get_public_key()
|
||||
inum = -1
|
||||
try:
|
||||
inum = self.bob.friend_add(self.alice._address, bytes(MSG, 'UTF-8'))
|
||||
assert inum >= 0, f"bob_add_alice_as_friend !>= 0 {inum}"
|
||||
# need a friend connected?
|
||||
if not self.get_connection_status():
|
||||
LOG.warning(f"test_groups_join NOT CONNECTED")
|
||||
self.loop_until_connected(self.bob)
|
||||
self.alice.callback_friend_request(alices_on_friend_request)
|
||||
if not self.wait_otox_attrs(self.bob, [sSlot]):
|
||||
LOG_WARN(f"bob_add_alice_as_friend NO setting {sSlot}")
|
||||
return False
|
||||
self.baid = self.bob.friend_by_public_key(apk)
|
||||
assert self.baid >= 0, self.baid
|
||||
assert self.bob.friend_exists(self.baid)
|
||||
assert not self.bob.friend_exists(self.baid + 1)
|
||||
assert self.bob.self_get_friend_list_size() >= 1
|
||||
assert self.baid in self.bob.self_get_friend_list()
|
||||
except Exception as e:
|
||||
LOG.error(f"bob_add_alice_as_friend EXCEPTION {e}")
|
||||
return False
|
||||
finally:
|
||||
self.bob.callback_friend_message(None)
|
||||
|
||||
return True
|
||||
|
||||
def alice_add_bob_as_friend(self) -> bool:
|
||||
"""
|
||||
t:friend_add
|
||||
t:on_friend_request
|
||||
t:friend_by_public_key
|
||||
"""
|
||||
MSG = 'Bob, this is Alice.'
|
||||
sSlot = 'friend_request'
|
||||
if not self.bAliceNeedAddBob(): return True
|
||||
|
||||
def bobs_on_friend_request(iTox,
|
||||
public_key,
|
||||
message_data,
|
||||
message_data_size,
|
||||
*largs) -> None:
|
||||
LOG_DEBUG(f"bobs_on_friend_request: " +repr(message_data))
|
||||
try:
|
||||
assert str(message_data, 'UTF-8') == MSG
|
||||
except Exception as e:
|
||||
LOG_WARN(f"bobs_on_friend_request: Exception {e}")
|
||||
# return
|
||||
setattr(self.alice, sSlot, True)
|
||||
|
||||
LOG_INFO(f"bobs_on_friend_request: {sSlot} = True ")
|
||||
setattr(self.alice, sSlot, None)
|
||||
bpk = self.bob.self_get_public_key()
|
||||
inum = -1
|
||||
try:
|
||||
inum = self.alice.friend_add(self.bob._address, bytes(MSG, 'UTF-8'))
|
||||
assert inum >= 0, f"alice.friend_add !>= 0 {inum}"
|
||||
self.bob.callback_friend_request(bobs_on_friend_request)
|
||||
if not self.wait_otox_attrs(self.alice, [sSlot]):
|
||||
LOG_WARN(f"alice.friend_add NO wait {sSlot}")
|
||||
return False
|
||||
self.abid = self.alice.friend_by_public_key(bpk)
|
||||
assert self.abid >= 0, self.abid
|
||||
assert self.alice.friend_exists(self.abid), "not exists"
|
||||
assert not self.alice.friend_exists(self.abid + 1), "exists +1"
|
||||
assert self.abid in self.alice.self_get_friend_list(), "not in list"
|
||||
assert self.alice.self_get_friend_list_size() >= 1, "list size"
|
||||
except Exception as e:
|
||||
LOG.error(f"alice.friend_add EXCEPTION {e}")
|
||||
return False
|
||||
finally:
|
||||
self.bob.callback_friend_message(None)
|
||||
return True
|
||||
|
||||
def bob_add_alice_as_friend_and_status(self) -> bool:
|
||||
if self.bob._args.norequest:
|
||||
assert self.bob_add_alice_as_friend_norequest()
|
||||
else:
|
||||
assert self.bob_add_alice_as_friend()
|
||||
|
||||
#: Wait until both are online
|
||||
sSlot = 'friend_status'
|
||||
setattr(self.bob, sSlot, None)
|
||||
def bobs_on_friend_status(iTox, friend_id, iStatus, *largs) -> None:
|
||||
LOG_INFO(f"bobs_on_friend_status {friend_id} ?>=0 iS={iStatus}")
|
||||
setattr(self.bob, sSlot, False)
|
||||
|
||||
|
||||
sSlot = 'friend_status'
|
||||
setattr(self.alice, sSlot, None)
|
||||
def alices_on_friend_status(iTox, friend_id, iStatus, *largs) -> None:
|
||||
LOG_INFO(f"alices_on_friend_status {friend_id} ?>=0 iS={iStatus}")
|
||||
setattr(self.alice, sSlot, False)
|
||||
|
||||
try:
|
||||
# need a friend connected?
|
||||
if not self.get_connection_status():
|
||||
self.loop_until_connected(self.bob)
|
||||
LOG.info("bob_add_alice_as_friend_and_status waiting for alice connections")
|
||||
if not self.wait_otox_attrs(self.alice,
|
||||
[ # 'friend_conn_status',
|
||||
'friend_status']):
|
||||
return False
|
||||
|
||||
# self.bob.callback_friend_connection_status(bobs_on_friend_connection_status)
|
||||
self.bob.callback_friend_status(bobs_on_friend_status)
|
||||
# self.alice.callback_friend_connection_status(alices_on_friend_connection_status)
|
||||
self.alice.callback_friend_status(alices_on_friend_status)
|
||||
|
||||
LOG.info("bob_add_alice_as_friend_and_status waiting for bob connections")
|
||||
if not self.wait_otox_attrs(self.bob,
|
||||
[ # 'friend_conn_status',
|
||||
'friend_status']):
|
||||
LOG_WARN('bob_add_alice_as_friend_and_status NO')
|
||||
# return False
|
||||
except Exception as e:
|
||||
LOG.error(f"bob_add_alice_as_friend_and_status ERROR {e}")
|
||||
return False
|
||||
finally:
|
||||
# self.alice.callback_friend_connection_status(None)
|
||||
# self.bob.callback_friend_connection_status(None)
|
||||
self.alice.callback_friend_status(None)
|
||||
self.bob.callback_friend_status(None)
|
||||
return True
|
||||
|
||||
def otox_test_groups_create(self,
|
||||
otox,
|
||||
group_name='test_group',
|
||||
nick='test_nick',
|
||||
topic='Test Topic', # str
|
||||
) -> int:
|
||||
privacy_state = enums.TOX_GROUP_PRIVACY_STATE['PUBLIC']
|
||||
|
||||
iGrp = otox.group_new(privacy_state, group_name, nick)
|
||||
assert iGrp >= 0
|
||||
LOG.info(f"group iGrp={iGrp}")
|
||||
|
||||
otox.group_set_topic(iGrp, topic)
|
||||
assert otox.group_get_topic(iGrp) == topic
|
||||
assert otox.group_get_topic_size(iGrp) == len(topic)
|
||||
|
||||
name = otox.group_get_name(iGrp)
|
||||
if type(name) == bytes:
|
||||
name = str(name, 'utf-8')
|
||||
assert name == group_name, name
|
||||
assert otox.group_get_name_size(iGrp) == len(group_name)
|
||||
|
||||
sPk = otox.group_self_get_public_key(iGrp)
|
||||
i = otox.group_get_password_size(iGrp)
|
||||
assert otox.group_get_password_size(iGrp) >= 0
|
||||
sP = otox.group_get_password(iGrp)
|
||||
assert len(sP) == i, sP
|
||||
assert otox.group_get_privacy_state(iGrp) == privacy_state
|
||||
i = otox.tox_group_get_topic_lock(iGrp)
|
||||
|
||||
assert otox.group_get_number_groups() > 0, "numg={otox.group_get_number_groups()}"
|
||||
LOG.info(f"group pK={sPk} iGrp={iGrp} numg={otox.group_get_number_groups()}")
|
||||
return iGrp
|
||||
|
||||
def otox_verify_group(self, otox, iGrp) -> None:
|
||||
"""
|
||||
group_self_get_name
|
||||
group_self_get_peer_id
|
||||
group_self_get_public_key
|
||||
group_self_get_role
|
||||
group_self_get_status
|
||||
group_self_set_name
|
||||
"""
|
||||
|
||||
group_number = iGrp
|
||||
try:
|
||||
assert type(iGrp) == int, "otox_test_groups_join iGrp not an int"
|
||||
assert iGrp < UINT32_MAX, "otox_test_groups_join iGrp failure UINT32_MAX"
|
||||
assert iGrp >= 0, f"otox_test_groups_join iGrp={iGrp} < 0"
|
||||
sGrp = otox.group_get_chat_id(iGrp)
|
||||
assert len(sGrp) == enums.TOX_GROUP_CHAT_ID_SIZE * 2, \
|
||||
f"group sGrp={sGrp} {len(sGrp)} != {enums.TOX_GROUP_CHAT_ID_SIZE * 2}"
|
||||
sPk = otox.group_self_get_public_key(iGrp)
|
||||
LOG.info(f"otox_verify_group sPk={sPk} iGrp={iGrp} n={otox.group_get_number_groups()}")
|
||||
|
||||
sName = otox.group_self_get_name(iGrp)
|
||||
iStat = otox.group_self_get_status(iGrp)
|
||||
iId = otox.group_self_get_peer_id(iGrp)
|
||||
iRole = otox.group_self_get_role(iGrp)
|
||||
iStat = otox.group_self_get_status(iGrp)
|
||||
LOG.info(f"otox_verify_group sName={sName} iStat={iStat} iId={iId} iRole={iRole} iStat={iStat}")
|
||||
|
||||
assert otox.group_self_set_name(iGrp, "NewName")
|
||||
|
||||
bRet = otox.group_is_connected(iGrp)
|
||||
except Exception as e:
|
||||
LOG.warn(f"group_is_connected EXCEPTION {e}")
|
||||
return -1
|
||||
# chat->connection_state == CS_CONNECTED || chat->connection_state == CS_CONNECTING;
|
||||
if not bRet:
|
||||
LOG.warn(f"group_is_connected WARN not connected iGrp={iGrp} n={otox.group_get_number_groups()}")
|
||||
else:
|
||||
LOG.info(f"group_is_connected SUCCESS connected iGrp={iGrp} n={otox.group_get_number_groups()}")
|
||||
try:
|
||||
bRet = self.group_until_connected(otox, iGrp, iMax=2*otox._args.test_timeout)
|
||||
except Exception as e:
|
||||
LOG.error(f"group_until_connected EXCEPTION {e}")
|
||||
return -1
|
||||
# chat->connection_state == CS_CONNECTED || chat->connection_state == CS_CONNECTING;
|
||||
if bRet:
|
||||
LOG.warn(f"group_until_connected WARN not connected iGrp={iGrp} n={otox.group_get_number_groups()}")
|
||||
else:
|
||||
LOG.info(f"group_until_connected SUCCESS connected iGrp={iGrp} n={otox.group_get_number_groups()}")
|
||||
|
||||
message = bytes('hello', 'utf-8')
|
||||
bRet = otox.group_send_message(iGrp, TOX_MESSAGE_TYPE['NORMAL'], message)
|
||||
if not bRet:
|
||||
LOG.warn(f"group_send_message {bRet}")
|
||||
else:
|
||||
LOG.debug(f"group_send_message {bRet}")
|
||||
|
||||
# 360497DA684BCE2A500C1AF9B3A5CE949BBB9F6FB1F91589806FB04CA039E313
|
||||
# 75D2163C19FEFFE51508046398202DDC321E6F9B6654E99BAE45FFEC134F05DE
|
||||
def otox_test_groups_join(self, otox,
|
||||
chat_id="75d2163c19feffe51508046398202ddc321e6f9b6654e99bae45ffec134f05de",
|
||||
nick='nick',
|
||||
topic='Test Topic', # str
|
||||
):
|
||||
status = ''
|
||||
password = ''
|
||||
LOG.debug(f"group_join nick={nick} chat_id={chat_id}")
|
||||
try:
|
||||
iGrp = otox.group_join(chat_id, password, nick, status)
|
||||
LOG.info(f"otox_test_groups_join SUCCESS iGrp={iGrp} chat_id={chat_id}")
|
||||
self.otox_verify_group(otox, iGrp)
|
||||
|
||||
except Exception as e:
|
||||
# gui
|
||||
LOG.error(f"otox_test_groups_join EXCEPTION {e}")
|
||||
raise
|
||||
|
||||
return iGrp
|
||||
|
||||
def otox_test_groups(self,
|
||||
otox,
|
||||
group_name='test_group',
|
||||
nick='test_nick',
|
||||
topic='Test Topic', # str
|
||||
) -> int:
|
||||
|
||||
try:
|
||||
iGrp = self.otox_test_groups_create(otox, group_name, nick, topic)
|
||||
self.otox_verify_group(otox, iGrp)
|
||||
except Exception as e:
|
||||
LOG.error(f"otox_test_groups ERROR {e}")
|
||||
raise
|
||||
|
||||
# unfinished
|
||||
# tox_group_peer_exit_cb
|
||||
# tox_callback_group_peer_join
|
||||
# tox.callback_group_peer_status
|
||||
# tox.callback_group_peer_name
|
||||
# tox.callback_group_peer_exit
|
||||
# tox.callback_group_peer_join
|
||||
return iGrp
|
||||
|
||||
def wait_friend_get_connection_status(self, otox, fid:int, n:int = iN) -> int:
|
||||
i = 0
|
||||
while i < n:
|
||||
i += 1
|
||||
iRet = otox.friend_get_connection_status(fid)
|
||||
if iRet == TOX_CONNECTION['NONE']:
|
||||
LOG.debug(f"wait_friend_get_connection_status NOT CONNECTED i={i} fid={fid} {iRet}")
|
||||
self.loop_until_connected(otox)
|
||||
else:
|
||||
LOG.info(f"wait_friend_get_connection_status fid={fid} {iRet}")
|
||||
return True
|
||||
else:
|
||||
LOG.error(f"wait_friend_get_connection_status fid={fid} n={n}")
|
||||
return False
|
||||
|
||||
def warn_if_no_cb(self, alice, sSlot:str) -> None:
|
||||
if not hasattr(alice, sSlot+'_cb') or \
|
||||
not getattr(alice, sSlot+'_cb'):
|
||||
LOG.warning(f"self.bob.{sSlot}_cb NOT EXIST")
|
||||
|
||||
def warn_if_cb(self, alice, sSlot:str) -> None:
|
||||
if hasattr(self.bob, sSlot+'_cb') and \
|
||||
getattr(self.bob, sSlot+'_cb'):
|
||||
LOG.warning(f"self.bob.{sSlot}_cb EXIST")
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,23 @@
|
||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||
|
||||
from ctypes import c_int, POINTER, c_void_p, byref, ArgumentError, c_uint32, CFUNCTYPE, c_size_t, c_uint8, c_uint16
|
||||
from ctypes import c_char_p, c_int32, c_bool, cast
|
||||
from 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 typing import Union, Callable
|
||||
|
||||
from wrapper.libtox import LibToxAV
|
||||
from wrapper.toxav_enums import *
|
||||
try:
|
||||
from toxygen_wrapper.libtox import LibToxAV
|
||||
import toxygen_wrapper.toxav_enums as enum
|
||||
except:
|
||||
from libtox import LibToxAV
|
||||
import toxav_enums as enum
|
||||
class ToxError(ArgumentError): pass
|
||||
|
||||
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)
|
||||
def LOG_ERROR(a: str) -> None: print('EROR> '+a)
|
||||
def LOG_WARN(a: str) -> None: print('WARN> '+a)
|
||||
def LOG_INFO(a: str) -> None: print('INFO> '+a)
|
||||
def LOG_DEBUG(a: str) -> None: print('DBUG> '+a)
|
||||
def LOG_TRACE(a: str) -> None: pass # print('DEBUGx: '+a)
|
||||
|
||||
class ToxAV:
|
||||
"""
|
||||
@ -20,9 +27,7 @@ class ToxAV:
|
||||
peers.
|
||||
"""
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Creation and destruction
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def __init__(self, tox_pointer):
|
||||
"""
|
||||
@ -36,20 +41,20 @@ class ToxAV:
|
||||
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']:
|
||||
if toxav_err_new == enum.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']:
|
||||
if toxav_err_new == enum.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.')
|
||||
if toxav_err_new == enum.TOXAV_ERR_NEW['MULTIPLE']:
|
||||
raise ToxError('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):
|
||||
def kill(self) -> None:
|
||||
"""
|
||||
Releases all resources associated with the A/V session.
|
||||
|
||||
@ -67,31 +72,27 @@ class ToxAV:
|
||||
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):
|
||||
def iteration_interval(self) -> int:
|
||||
"""
|
||||
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)
|
||||
return int(self.libtoxav.toxav_iteration_interval(self._toxav_pointer))
|
||||
|
||||
def iterate(self):
|
||||
def iterate(self) -> None:
|
||||
"""
|
||||
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):
|
||||
def call(self, friend_number: int, audio_bit_rate: int, video_bit_rate: int) -> bool:
|
||||
"""
|
||||
Call a friend. This will start ringing the friend.
|
||||
|
||||
@ -109,23 +110,24 @@ class ToxAV:
|
||||
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']:
|
||||
if toxav_err_call == enum.TOXAV_ERR_CALL['OK']:
|
||||
return bool(result)
|
||||
elif toxav_err_call == TOXAV_ERR_CALL['MALLOC']:
|
||||
if toxav_err_call == enum.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']:
|
||||
if toxav_err_call == enum.TOXAV_ERR_CALL['SYNC']:
|
||||
raise ToxError('Synchronization error occurred.')
|
||||
if toxav_err_call == enum.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']:
|
||||
if toxav_err_call == enum.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']:
|
||||
if toxav_err_call == enum.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']:
|
||||
if toxav_err_call == enum.TOXAV_ERR_CALL['INVALID_BIT_RATE']:
|
||||
raise ArgumentError('Audio or video bit rate is invalid.')
|
||||
raise ArgumentError('The function did not return OK')
|
||||
|
||||
def callback_call(self, callback, user_data):
|
||||
def callback_call(self, callback: Union[Callable,None], user_data) -> None:
|
||||
"""
|
||||
Set the callback for the `call` event. Pass None to unset.
|
||||
|
||||
@ -147,7 +149,7 @@ class ToxAV:
|
||||
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):
|
||||
def answer(self, friend_number: int, audio_bit_rate: int, video_bit_rate: int) -> bool:
|
||||
"""
|
||||
Accept an incoming call.
|
||||
|
||||
@ -161,29 +163,31 @@ class ToxAV:
|
||||
"""
|
||||
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))
|
||||
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']:
|
||||
if toxav_err_answer == enum.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 '
|
||||
if toxav_err_answer == enum.TOXAV_ERR_ANSWER['SYNC']:
|
||||
raise ToxError('Synchronization error occurred.')
|
||||
if toxav_err_answer == enum.TOXAV_ERR_ANSWER['CODEC_INITIALIZATION']:
|
||||
raise ToxError('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']:
|
||||
if toxav_err_answer == enum.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']:
|
||||
if toxav_err_answer == enum.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']:
|
||||
if toxav_err_answer == enum.TOXAV_ERR_ANSWER['INVALID_BIT_RATE']:
|
||||
raise ArgumentError('Audio or video bit rate is invalid.')
|
||||
raise ToxError('The function did not return OK')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Call state graph
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def callback_call_state(self, callback, user_data):
|
||||
def callback_call_state(self, callback: Union[Callable,None], user_data) -> None:
|
||||
"""
|
||||
Set the callback for the `call_state` event. Pass None to unset.
|
||||
|
||||
@ -206,11 +210,9 @@ class ToxAV:
|
||||
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):
|
||||
def call_control(self, friend_number: int, control: int) -> bool:
|
||||
"""
|
||||
Sends a call control command to a friend.
|
||||
|
||||
@ -220,31 +222,30 @@ class ToxAV:
|
||||
"""
|
||||
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))
|
||||
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']:
|
||||
if toxav_err_call_control == enum.TOXAV_ERR_CALL_CONTROL['OK']:
|
||||
return True
|
||||
if toxav_err_call_control == enum.TOXAV_ERR_CALL_CONTROL['SYNC']:
|
||||
raise ToxError('Synchronization error occurred.')
|
||||
if toxav_err_call_control == enum.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, '
|
||||
if toxav_err_call_control == enum.TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_IN_CALL']:
|
||||
raise ToxError('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 '
|
||||
if toxav_err_call_control == enum.TOXAV_ERR_CALL_CONTROL['INVALID_TRANSITION']:
|
||||
raise ToxError('Happens if user tried to pause an already paused call or if trying to resume a call '
|
||||
'that is not paused.')
|
||||
raise ToxError('The function did not return OK.')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# TODO Controlling bit rates
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# A/V sending
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def audio_send_frame(self, friend_number, pcm, sample_count, channels, sampling_rate):
|
||||
def audio_send_frame(self, friend_number: int, pcm, sample_count: int, channels: int, sampling_rate: int) -> bool:
|
||||
"""
|
||||
Send an audio frame to a friend.
|
||||
|
||||
@ -270,26 +271,27 @@ class ToxAV:
|
||||
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']:
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['OK']:
|
||||
return bool(result)
|
||||
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']:
|
||||
if toxav_err_send_frame == enum.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']:
|
||||
if toxav_err_send_frame == enum.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']:
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']:
|
||||
raise ToxError('This client is currently not in a call with the friend.')
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['SYNC']:
|
||||
raise ToxError('Synchronization error occurred.')
|
||||
if toxav_err_send_frame == enum.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'
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']:
|
||||
raise ToxError('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.')
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['RTP_FAILED']:
|
||||
ToxError('Failed to push frame through rtp interface.')
|
||||
raise ToxError('The function did not return OK.')
|
||||
|
||||
def video_send_frame(self, friend_number, width, height, y, u, v):
|
||||
def video_send_frame(self, friend_number: int, width: int, height: int, y, u, v) -> bool:
|
||||
"""
|
||||
Send a video frame to a friend.
|
||||
|
||||
@ -306,34 +308,38 @@ class ToxAV:
|
||||
"""
|
||||
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))
|
||||
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']:
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['OK']:
|
||||
return bool(result)
|
||||
elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']:
|
||||
if toxav_err_send_frame == enum.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']:
|
||||
if toxav_err_send_frame == enum.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']:
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']:
|
||||
raise ToxError('This client is currently not in a call with the friend.')
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['SYNC']:
|
||||
raise ToxError('Synchronization error occurred.')
|
||||
if toxav_err_send_frame == enum.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'
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']:
|
||||
raise ToxError('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.')
|
||||
if toxav_err_send_frame == enum.TOXAV_ERR_SEND_FRAME['RTP_FAILED']:
|
||||
ToxError('Failed to push frame through rtp interface.')
|
||||
raise ToxError('The function did not return OK.')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# A/V receiving
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def callback_audio_receive_frame(self, callback, user_data):
|
||||
def callback_audio_receive_frame(self, callback: Union[Callable,None], user_data) -> None:
|
||||
"""
|
||||
Set the callback for the `audio_receive_frame` event. Pass None to unset.
|
||||
|
||||
@ -352,7 +358,9 @@ class ToxAV:
|
||||
: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.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")
|
||||
@ -360,7 +368,7 @@ class ToxAV:
|
||||
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):
|
||||
def callback_video_receive_frame(self, callback: Union[Callable,None], user_data) -> None:
|
||||
"""
|
||||
Set the callback for the `video_receive_frame` event. Pass None to unset.
|
||||
|
@ -59,8 +59,15 @@ TOX_ERR_SET_INFO = {
|
||||
'OK': 0,
|
||||
'NULL': 1,
|
||||
'TOO_LONG': 2,
|
||||
# The function returned successfully.
|
||||
'TOX_ERR_SET_INFO_OK': 0,
|
||||
# One of the arguments to the function was NULL when it was not expected.
|
||||
'TOX_ERR_SET_INFO_NULL': 1,
|
||||
# Information length exceeded maximum permissible size.
|
||||
'TOX_ERR_SET_INFO_TOO_LONG': 2,
|
||||
}
|
||||
|
||||
|
||||
TOX_ERR_FRIEND_ADD = {
|
||||
'OK': 0,
|
||||
'NULL': 1,
|
||||
@ -302,6 +309,11 @@ TOX_ERR_GROUP_JOIN = {
|
||||
'TOX_ERR_GROUP_JOIN_TOO_LONG': 3,
|
||||
}
|
||||
|
||||
TOX_ERR_GROUP_IS_CONNECTED = {
|
||||
'TOX_ERR_GROUP_IS_CONNECTED_OK': 0,
|
||||
'TOX_ERR_GROUP_IS_CONNECTED_GROUP_NOT_FOUND': 1
|
||||
}
|
||||
|
||||
TOX_ERR_GROUP_RECONNECT = {
|
||||
|
||||
#
|
||||
@ -315,6 +327,19 @@ TOX_ERR_GROUP_RECONNECT = {
|
||||
'TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND': 1,
|
||||
}
|
||||
|
||||
TOX_ERR_GROUP_DISCONNECT = {
|
||||
|
||||
# The function returned successfully.
|
||||
'TOX_ERR_GROUP_DISCONNECT_OK': 0,
|
||||
|
||||
# The group number passed did not designate a valid group.
|
||||
'TOX_ERR_GROUP_DISCONNECT_GROUP_NOT_FOUND': 1,
|
||||
|
||||
# The group is already disconnected.
|
||||
'TOX_ERR_GROUP_DISCONNECT_ALREADY_DISCONNECTED': 2,
|
||||
}
|
||||
|
||||
|
||||
TOX_ERR_GROUP_LEAVE = {
|
||||
|
||||
#
|
||||
@ -438,7 +463,7 @@ TOX_ERR_GROUP_STATE_QUERIES = {
|
||||
#
|
||||
'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
|
@ -1,91 +1,91 @@
|
||||
# -*- 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 *
|
||||
from toxygen_wrapper import libtox
|
||||
import toxygen_wrapper.toxencryptsave_enums_and_consts as enum
|
||||
except:
|
||||
import libtox
|
||||
from toxencryptsave_enums_and_consts import *
|
||||
import toxencryptsave_enums_and_consts as enum
|
||||
|
||||
try:
|
||||
from ctypes import c_size_t
|
||||
from ctypes import create_string_buffer
|
||||
from ctypes import byref
|
||||
from ctypes import c_int
|
||||
from ctypes import ArgumentError
|
||||
from ctypes import c_char_p
|
||||
from ctypes import c_bool
|
||||
except:
|
||||
print("failed to import ctypes")
|
||||
raise
|
||||
from typing import Union, Callable
|
||||
from ctypes import (ArgumentError, byref, c_bool, c_char_p, c_int, c_size_t,
|
||||
create_string_buffer, Array)
|
||||
def ToxError(ArgumentError): pass
|
||||
|
||||
|
||||
class ToxEncryptSave:
|
||||
|
||||
def __init__(self):
|
||||
self.libtoxencryptsave = libtox.LibToxEncryptSave()
|
||||
|
||||
def is_data_encrypted(self, data):
|
||||
def is_data_encrypted(self, data: bytes) -> bool:
|
||||
"""
|
||||
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
|
||||
return bool(result)
|
||||
|
||||
def pass_encrypt(self, data, password):
|
||||
def pass_encrypt(self, data: bytes, password: Union[str,bytes]) -> bytes:
|
||||
"""
|
||||
Encrypts the given data with the given password.
|
||||
|
||||
:return: output array
|
||||
"""
|
||||
out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||
out = create_string_buffer(len(data) + enum.TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||
tox_err_encryption = c_int()
|
||||
assert password
|
||||
if type(password) != bytes:
|
||||
password = bytes(password, 'utf-8')
|
||||
self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data),
|
||||
c_size_t(len(data)),
|
||||
c_char_p(bytes(password, 'utf-8')),
|
||||
c_char_p(password),
|
||||
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']:
|
||||
if tox_err_encryption == enum.TOX_ERR_ENCRYPTION['OK']:
|
||||
return bytes(out[:])
|
||||
if tox_err_encryption == enum.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']:
|
||||
if tox_err_encryption == enum.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']:
|
||||
if tox_err_encryption == enum.TOX_ERR_ENCRYPTION['FAILED']:
|
||||
raise RuntimeError('The encryption itself failed.')
|
||||
raise ToxError('The function did not return OK.')
|
||||
|
||||
def pass_decrypt(self, data, password):
|
||||
def pass_decrypt(self, data: bytes, password: Union[str,bytes]) -> bytes:
|
||||
"""
|
||||
Decrypts the given data with the given password.
|
||||
|
||||
:return: output array
|
||||
"""
|
||||
out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||
out = create_string_buffer(len(data) - enum.TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||
tox_err_decryption = c_int()
|
||||
assert password
|
||||
if type(password) != bytes:
|
||||
password = bytes(password, 'utf-8')
|
||||
self.libtoxencryptsave.tox_pass_decrypt(c_char_p(bytes(data)),
|
||||
c_size_t(len(data)),
|
||||
c_char_p(bytes(password, 'utf-8')),
|
||||
c_char_p(password),
|
||||
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']:
|
||||
if tox_err_decryption == enum.TOX_ERR_DECRYPTION['OK']:
|
||||
return bytes(out[:])
|
||||
if tox_err_decryption == enum.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']:
|
||||
if tox_err_decryption == enum.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']:
|
||||
if tox_err_decryption == enum.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']:
|
||||
if tox_err_decryption == enum.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']:
|
||||
if tox_err_decryption == enum.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.')
|
||||
raise ToxError('The function did not return OK.')
|
455
src/toxygen_wrapper/toxygen_echo.py
Normal file
455
src/toxygen_wrapper/toxygen_echo.py
Normal file
@ -0,0 +1,455 @@
|
||||
#!/var/local/bin/python3.bash
|
||||
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
||||
|
||||
# A work in progress - chat works, but I don't think AV does.
|
||||
|
||||
""" echo.py a basic Tox echo service. 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
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
import threading
|
||||
import random
|
||||
from ctypes import *
|
||||
import time
|
||||
from typing import Union, Callable
|
||||
|
||||
# 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 toxygen_wrapper import tox
|
||||
import toxygen_wrapper.toxcore_enums_and_consts as enums
|
||||
from toxygen_wrapper.tox import Tox, UINT32_MAX
|
||||
from toxygen_wrapper.toxcore_enums_and_consts import TOX_CONNECTION, TOX_USER_STATUS, \
|
||||
TOX_MESSAGE_TYPE, TOX_PUBLIC_KEY_SIZE, TOX_FILE_CONTROL, TOX_FILE_KIND
|
||||
|
||||
import toxygen_wrapper.tests.support_testing as ts
|
||||
from toxygen_wrapper.tests.support_testing import oMainArgparser
|
||||
|
||||
def sleep(fSec) -> None:
|
||||
if 'QtCore' in globals():
|
||||
if fSec > .000001: QtCore.QThread.msleep(fSec)
|
||||
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
|
||||
|
||||
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 = False
|
||||
iDHT_TRIES = 100
|
||||
iDHT_TRY = 0
|
||||
|
||||
#?SERVER = lLOCAL[-1]
|
||||
|
||||
if not bHAVE_AV:
|
||||
class AV(): pass
|
||||
else:
|
||||
class AV(tox.ToxAV):
|
||||
def __init__(self, core):
|
||||
super(AV, self).__init__(core)
|
||||
self.core = self.get_tox()
|
||||
|
||||
def on_call(self, fid:int, audio_enabled:bool, video_enabled:bool) -> None:
|
||||
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}")
|
||||
|
||||
def on_call_state(self, fid:int, state:int) -> None:
|
||||
LOG.info('call state:fn=%d, state=%d' % (fid, state))
|
||||
|
||||
def on_audio_bit_rate(self, fid:int, audio_bit_rate:int) -> None:
|
||||
LOG.info('audio bit rate status: fn=%d, abr=%d' %
|
||||
(fid, audio_bit_rate))
|
||||
|
||||
def on_video_bit_rate(self, fid:int, video_bit_rate:int) -> None:
|
||||
LOG.info('video bit rate status: fn=%d, vbr=%d' %
|
||||
(fid, video_bit_rate))
|
||||
|
||||
def on_audio_receive_frame(self, fid:int,
|
||||
pcm:int,
|
||||
sample_count:int,
|
||||
channels:int,
|
||||
sampling_rate:int) -> None:
|
||||
# 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:int, width:int, height:int, frame, u, v) -> None:
|
||||
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) -> None:
|
||||
self.iterate()
|
||||
|
||||
|
||||
def save_to_file(tox, fname: str) -> None:
|
||||
data = tox.get_savedata()
|
||||
with open(fname, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
def load_from_file(fname: str) -> bytes:
|
||||
assert os.path.exists(fname)
|
||||
return open(fname, 'rb').read()
|
||||
|
||||
class EchoBot():
|
||||
def __init__(self, oTox):
|
||||
self._tox = oTox
|
||||
self._tox.self_set_name("PyEchoBot")
|
||||
LOG.info(f'ID: {self._tox.self_get_address()}')
|
||||
|
||||
self.files = {}
|
||||
self.av = None
|
||||
self.on_connection_status = None
|
||||
|
||||
def start(self) -> None:
|
||||
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) -> None:
|
||||
key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE])
|
||||
sPk = 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) -> None:
|
||||
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) -> None:
|
||||
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} {filenumber} {kind} {size} {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) -> None:
|
||||
if not self.on_connection_status:
|
||||
def on_connection_status(iTox, iCon, *largs) -> None:
|
||||
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 = ts.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(lElts)
|
||||
for lElt in lElts[: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) -> None:
|
||||
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:int = 100) -> None:
|
||||
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: Union[bytes,str], message: Union[bytes,str]) -> None:
|
||||
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: int, message_type: int, message: Union[bytes,str]) -> None:
|
||||
name = self._tox.friend_get_name(friendId)
|
||||
LOG.debug(f"{name}, {message}, {message_type}")
|
||||
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: int, filenumber, position, data) -> None:
|
||||
filename = self.files[(fid, filenumber)]['filename']
|
||||
size = self.files[(fid, filenumber)]['size']
|
||||
LOG.debug(f"on_file_recv_chunk {fid} {filenumber} {filename} {position/float(size)*100}")
|
||||
|
||||
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._tox.file_send(fid, TOX_FILE_KIND['DATA'], length, filename)
|
||||
|
||||
del self.files[(fid, filenumber)]
|
||||
return
|
||||
|
||||
self.files[(fid, filenumber)]['f'] += data
|
||||
|
||||
class App():
|
||||
def __init__(self):
|
||||
self.mode = 0
|
||||
oAPP = App()
|
||||
|
||||
class EchobotTox(Tox):
|
||||
|
||||
def __init__(self, opts, app=None):
|
||||
|
||||
super().__init__(opts, app=app)
|
||||
self._address = self.self_get_address()
|
||||
self.name = 'pyechobot'
|
||||
self._opts = opts
|
||||
self._app = app
|
||||
|
||||
class BaseThread(threading.Thread):
|
||||
|
||||
def __init__(self, name=None, target=None):
|
||||
if name:
|
||||
super().__init__(name=name, target=target)
|
||||
else:
|
||||
super().__init__(target=target)
|
||||
self._stop_thread = False
|
||||
self.name = name
|
||||
|
||||
def stop_thread(self, timeout=-1) -> None:
|
||||
self._stop_thread = True
|
||||
if timeout < 0:
|
||||
timeout = ts.iTHREAD_TIMEOUT
|
||||
i = 0
|
||||
while i < ts.iTHREAD_JOINS:
|
||||
self.join(timeout)
|
||||
if not self.is_alive(): break
|
||||
i = i + 1
|
||||
else:
|
||||
LOG.warning(f"{self.name} BLOCKED")
|
||||
|
||||
class ToxIterateThread(BaseThread):
|
||||
|
||||
def __init__(self, tox):
|
||||
super().__init__(name='ToxIterateThread')
|
||||
self._tox = tox
|
||||
|
||||
def run(self) -> None:
|
||||
while not self._stop_thread:
|
||||
self._tox.iterate()
|
||||
sleep(self._tox.iteration_interval() / 1000)
|
||||
|
||||
def oArgparse(lArgv):
|
||||
parser = ts.oMainArgparser()
|
||||
parser.add_argument('profile', type=str, nargs='?', default=None,
|
||||
help='Path to Tox profile')
|
||||
oArgs = parser.parse_args(lArgv)
|
||||
ts.clean_booleans(oArgs)
|
||||
|
||||
if hasattr(oArgs, 'sleep'):
|
||||
if oArgs.sleep == 'qt':
|
||||
pass # broken or gevent.sleep(idle_period)
|
||||
elif oArgs.sleep == 'gevent':
|
||||
pass # broken or gevent.sleep(idle_period)
|
||||
else:
|
||||
oArgs.sleep = 'time'
|
||||
|
||||
return oArgs
|
||||
|
||||
def iMain(oArgs) -> int:
|
||||
global sDATA_FILE
|
||||
# oTOX_OPTIONS = ToxOptions()
|
||||
global oTOX_OPTIONS
|
||||
oMainArgparser
|
||||
oTOX_OPTIONS = ts.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:
|
||||
oTox = EchobotTox(opts, app=oAPP)
|
||||
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"ERROR {e}")
|
||||
iRet = 1
|
||||
except Exception as e:
|
||||
LOG.error(f"EXCEPTION {e}")
|
||||
LOG.warn(' iMain(): ' \
|
||||
+'\n' + traceback.format_exc())
|
||||
iRet = 1
|
||||
return iRet
|
||||
|
||||
def main(lArgs=None) -> int:
|
||||
global oTOX_OARGS
|
||||
global oTOX_OPTIONS
|
||||
global bIS_LOCAL
|
||||
if lArgs is None: lArgs = []
|
||||
oArgs = oArgparse(lArgs)
|
||||
bIS_LOCAL = oArgs.network in ['newlocal', 'localnew', 'local']
|
||||
oTOX_OARGS = oArgs
|
||||
setattr(oTOX_OARGS, 'bIS_LOCAL', bIS_LOCAL)
|
||||
oTOX_OPTIONS = ts.oToxygenToxOptions(oArgs)
|
||||
if coloredlogs:
|
||||
# https://pypi.org/project/coloredlogs/
|
||||
coloredlogs.install(level=oArgs.loglevel,
|
||||
logger=LOG,
|
||||
# %(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__':
|
||||
try:
|
||||
i = main(sys.argv[1:])
|
||||
except KeyboardInterrupt as e:
|
||||
i = 0
|
||||
except Exception as e:
|
||||
i = 1
|
||||
sys.exit(i)
|
116
tox.c-toxcore.missing
Normal file
116
tox.c-toxcore.missing
Normal file
@ -0,0 +1,116 @@
|
||||
tox_callback_conference_connected
|
||||
tox_callback_conference_invite
|
||||
tox_callback_conference_message
|
||||
tox_callback_conference_peer_list_changed
|
||||
tox_callback_conference_peer_name
|
||||
tox_callback_conference_title
|
||||
tox_callback_group_custom_private_packet
|
||||
tox_conference_by_id
|
||||
tox_conference_by_uid
|
||||
tox_conference_delete
|
||||
tox_conference_get_chatlist
|
||||
tox_conference_get_chatlist_size
|
||||
tox_conference_get_id
|
||||
tox_conference_get_title
|
||||
tox_conference_get_title_size
|
||||
tox_conference_get_type
|
||||
tox_conference_get_uid
|
||||
tox_conference_id_size
|
||||
tox_conference_invite
|
||||
tox_conference_join
|
||||
tox_conference_new
|
||||
tox_conference_offline_peer_count
|
||||
tox_conference_offline_peer_get_last_active
|
||||
tox_conference_offline_peer_get_name
|
||||
tox_conference_offline_peer_get_name_size
|
||||
tox_conference_offline_peer_get_public_key
|
||||
tox_conference_peer_count
|
||||
tox_conference_peer_get_name
|
||||
tox_conference_peer_get_name_size
|
||||
tox_conference_peer_get_public_key
|
||||
tox_conference_peer_number_is_ours
|
||||
tox_conference_send_message
|
||||
tox_conference_set_max_offline
|
||||
tox_conference_set_title
|
||||
tox_conference_uid_size
|
||||
tox_file_seek
|
||||
tox_get_salt
|
||||
tox_group_send_custom_private_packet
|
||||
tox_is_data_encrypted
|
||||
tox_options_get_dht_announcements_enabled
|
||||
tox_options_get_end_port
|
||||
tox_options_get_experimental_groups_persistence
|
||||
tox_options_get_experimental_thread_safety
|
||||
tox_options_get_hole_punching_enabled
|
||||
tox_options_get_ipv6_enabled
|
||||
tox_options_get_local_discovery_enabled
|
||||
tox_options_get_log_callback
|
||||
tox_options_get_log_user_data
|
||||
tox_options_get_operating_system
|
||||
tox_options_get_proxy_host
|
||||
tox_options_get_proxy_port
|
||||
tox_options_get_proxy_type
|
||||
tox_options_get_savedata_data
|
||||
tox_options_get_savedata_length
|
||||
tox_options_get_savedata_type
|
||||
tox_options_get_start_port
|
||||
tox_options_get_tcp_port
|
||||
tox_options_get_udp_enabled
|
||||
tox_options_set_dht_announcements_enabled
|
||||
tox_options_set_end_port
|
||||
tox_options_set_experimental_groups_persistence
|
||||
tox_options_set_experimental_thread_safety
|
||||
tox_options_set_hole_punching_enabled
|
||||
tox_options_set_ipv6_enabled
|
||||
tox_options_set_local_discovery_enabled
|
||||
tox_options_set_log_callback
|
||||
tox_options_set_log_user_data
|
||||
tox_options_set_operating_system
|
||||
tox_options_set_proxy_host
|
||||
tox_options_set_proxy_port
|
||||
tox_options_set_proxy_type
|
||||
tox_options_set_savedata_data
|
||||
tox_options_set_savedata_length
|
||||
tox_options_set_savedata_type
|
||||
tox_options_set_start_port
|
||||
tox_options_set_tcp_port
|
||||
tox_options_set_udp_enabled
|
||||
tox_pass_decrypt
|
||||
tox_pass_encrypt
|
||||
tox_pass_encryption_extra_length
|
||||
tox_pass_key_decrypt
|
||||
tox_pass_key_derive
|
||||
tox_pass_key_derive_with_salt
|
||||
tox_pass_key_encrypt
|
||||
tox_pass_key_free
|
||||
tox_pass_key_length
|
||||
tox_pass_salt_length
|
||||
tox_version_is_compatible
|
||||
toxav_add_av_groupchat
|
||||
toxav_answer
|
||||
toxav_audio_iterate
|
||||
toxav_audio_iteration_interval
|
||||
toxav_audio_send_frame
|
||||
toxav_audio_set_bit_rate
|
||||
toxav_call
|
||||
toxav_call_control
|
||||
toxav_callback_audio_bit_rate
|
||||
toxav_callback_audio_receive_frame
|
||||
toxav_callback_call
|
||||
toxav_callback_call_state
|
||||
toxav_callback_video_bit_rate
|
||||
toxav_callback_video_receive_frame
|
||||
toxav_get_tox
|
||||
toxav_group_send_audio
|
||||
toxav_groupchat_av_enabled
|
||||
toxav_groupchat_disable_av
|
||||
toxav_groupchat_enable_av
|
||||
toxav_iterate
|
||||
toxav_iteration_interval
|
||||
toxav_join_av_groupchat
|
||||
toxav_kill
|
||||
toxav_new
|
||||
toxav_video_iterate
|
||||
toxav_video_iteration_interval
|
||||
toxav_video_send_frame
|
||||
toxav_video_set_bit_rate
|
@ -1,80 +0,0 @@
|
||||
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
|
@ -1,19 +0,0 @@
|
||||
# @file echo.py
|
||||
# @author Wei-Ning Huang (AZ) <aitjcize@gmail.com>
|
||||
#
|
||||
# Copyright (C) 2013 - 2014 Wei-Ning Huang (AZ) <aitjcize@gmail.com>
|
||||
# All Rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
@ -1,65 +0,0 @@
|
||||
These are the tests taken from <https://github.com/oxij/PyTox>
|
||||
forked from https://github.com/aitjcize/PyTox by Wei-Ning Huang
|
||||
<aitjcize@gmail.com>. They have been converted to work with ```toxygen```.
|
||||
|
||||
All of the dependencies on ```toxygen``` should have been removed,
|
||||
but if you copy this directory to a subdirectory of ```toxygen/toxygen```
|
||||
then the tests may expand to testing some features of ```toxygen``` as well.
|
||||
The tests are good examples of how to run Tox in Python.
|
||||
|
||||
You can run the tests by running ```tests_socks.py```.
|
||||
|
||||
```
|
||||
usage: tests_socks.py [-h] [--proxy_host PROXY_HOST] [--proxy_port PROXY_PORT]
|
||||
[--proxy_type {0,1,2}] [--udp_enabled {True,False}]
|
||||
[--ipv6_enabled {False,False}]
|
||||
[--download_nodes_list {True,False}]
|
||||
[--nodes_json NODES_JSON]
|
||||
[--network {old,new,local,newlocal}]
|
||||
[--download_nodes_url DOWNLOAD_NODES_URL]
|
||||
[--logfile LOGFILE] [--loglevel LOGLEVEL]
|
||||
[--tcp_port TCP_PORT] [--mode MODE]
|
||||
[--sleep {qt,gevent,time}]
|
||||
[profile]
|
||||
|
||||
positional arguments:
|
||||
profile Path to Tox profileoptional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--proxy_host PROXY_HOST, --proxy-host PROXY_HOST
|
||||
proxy host
|
||||
--proxy_port PROXY_PORT, --proxy-port PROXY_PORT
|
||||
proxy port
|
||||
--proxy_type {0,1,2}, --proxy-type {0,1,2}
|
||||
proxy type 1=http, 2=socks
|
||||
--udp_enabled {True,False}
|
||||
En/Disable udp
|
||||
--ipv6_enabled {False,False}
|
||||
En/Disable ipv6 - default False
|
||||
--download_nodes_list {True,False}
|
||||
Download nodes list
|
||||
--nodes_json NODES_JSON
|
||||
--network {old,new,local,newlocal}
|
||||
--download_nodes_url DOWNLOAD_NODES_URL
|
||||
--logfile LOGFILE Filename for logging
|
||||
--loglevel LOGLEVEL Threshold for logging (lower is more) default: 20
|
||||
--tcp_port TCP_PORT, --tcp-port TCP_PORT
|
||||
--mode MODE Mode: 0=chat 1=chat+audio 2=chat+audio+video default:
|
||||
0
|
||||
--sleep {qt,gevent,time}
|
||||
Sleep method - one of qt, gevent , time
|
||||
```
|
||||
|
||||
Look at the ```@unittest``` decorators in the code for tests that
|
||||
are known to fail, or are unfinished. They will be skipped, but should
|
||||
all be eventually fixed and made to work. It has been tested with UDP and
|
||||
TCP proxy (Tor), and with Tor **you may have transient errors** due to
|
||||
connectivity - simply rerun with a sacrificed goat entrails on the keyboard.
|
||||
|
||||
It has ***not*** been tested on Windows, and there may be some minor breakage,
|
||||
which should be easy to fix.
|
||||
|
||||
Currently:
|
||||
```
|
||||
Ran 34 tests in 86.589s
|
||||
OK (skipped=12)
|
||||
```
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user