diff --git a/.gitignore b/.gitignore index 7cee62d..f9f3975 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ telegram *.o Makefile +aclocal.m4 autom4te.cache config.h config.log @@ -16,4 +17,5 @@ debian/telegram-cli.[a-z] debian/files debian/telegram-cli/* debian/telegram-cli.debhelper.log -debian/telegram-cli.substvars \ No newline at end of file +debian/telegram-cli.substvars +__pycache__ diff --git a/.travis.yml b/.travis.yml index a299f36..51df285 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ install: - sudo apt-get install libreadline6-dev - sudo apt-get install libssl-dev - sudo apt-get install liblua5.2-dev lua5.2 + - sudo apt-get install python-dev python - sudo apt-get install libevent-dev - sudo apt-get install libjansson-dev diff --git a/Makefile.in b/Makefile.in index e1989ab..c032397 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,10 +4,9 @@ CFLAGS=@CFLAGS@ LDFLAGS=@LDFLAGS@ @OPENSSL_LDFLAGS@ CPPFLAGS=@CPPFLAGS@ @OPENSSL_INCLUDES@ DEFS=@DEFS@ -COMPILE_FLAGS=${CFLAGS} ${CPFLAGS} ${CPPFLAGS} ${DEFS} -Wall -Wextra -Werror -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC - +COMPILE_FLAGS=${CFLAGS} ${CPFLAGS} ${CPPFLAGS} ${DEFS} -Wall -Werror -Wextra -Wno-missing-field-initializers -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC EXTRA_LIBS=@LIBS@ @EXTRA_LIBS@ @OPENSSL_LIBS@ -LOCAL_LDFLAGS=-rdynamic -ggdb -levent ${EXTRA_LIBS} +LOCAL_LDFLAGS=-rdynamic -ggdb -levent ${EXTRA_LIBS} -ldl -lpthread -lutil LINK_FLAGS=${LDFLAGS} ${LOCAL_LDFLAGS} DEP=dep @@ -19,7 +18,7 @@ DIR_LIST=${DEP} ${AUTO} ${EXE} ${OBJ} ${LIB} ${DEP}/auto ${OBJ}/auto EXE_LIST=${EXE}/telegram-cli -TG_OBJECTS=${OBJ}/main.o ${OBJ}/loop.o ${OBJ}/interface.o ${OBJ}/lua-tg.o ${OBJ}/json-tg.o +TG_OBJECTS=${OBJ}/main.o ${OBJ}/loop.o ${OBJ}/interface.o ${OBJ}/lua-tg.o ${OBJ}/json-tg.o ${OBJ}/python-tg.o ${OBJ}/python-types.o INCLUDE=-I. -I${srcdir} -I${srcdir}/tgl CC=@CC@ diff --git a/README-PY b/README-PY new file mode 100644 index 0000000..ae4467b --- /dev/null +++ b/README-PY @@ -0,0 +1,95 @@ +To use python with client you should write python script. You can specify it from config ("python_script" option) or from command_line [-Z]. + +You should set the following callbacks in your script: + tgl.on_binlog_replay_end() - it is called when replay of old events end. Any updates prior this call were already received by this client + some time ago. + + tgl.on_get_difference_end() - it is called after first get_difference call. So we received all updates after last client execute. + + tgl.on_our_id(our_id) - Informs about id of currently logged in user. + + tgl.on_msg_receive(msg) - it is called when we receive new msg (!! may be called before on_binlog_replay_end, than it is old msg). + + tgl.on_user_update(peer, what_changed) - updated info about user. what_changed is array of strings. + tgl.on_chat_update(peer, what_changed) - updated info about user. what_changed is array of strings. + tgl.on_secret_chat_update(peer, what_changed) - updated info about user. what_changed is array of strings. + + + +Also, you can call several functions. Each this function last two arguments, are callback, which is a python function implementing the callback from the function. +These functions may return false immidiately if something is bad with args, or return true and call cb_function in future. +The callback function should have one arguments: first success (True or False), and the rest depends on the call. +Functions that require a peer type other than what is passed will raise tgl.PeerError. + + +Function_list (arguments are listed aside from callback, import tgl for access) : + tgl.get_contact_list () + tgl.get_dialog_list () + + tgl.rename_chat (peer, new_name) + tgl.chat_set_photo (peer, file) + + tgl.send_typing (peer) + tgl.send_typing_abort (peer) + + tgl.send_msg (peer, text) + tgl.fwd_msg (peer, msg) + + tgl.send_photo (peer, file) + tgl.send_video (peer, file) + tgl.send_audio (peer, file) + tgl.send_document (peer, file) + tgl.send_text (peer, file) + + tgl.load_photo(msg) + tgl.load_video(msg) + tgl.load_video_thumb(msg) + tgl.load_audio(msg) + tgl.load_document(msg) + tgl.load_document_thumb(msg) + + tgl.info (peer) + + tgl.get_history (peer, limit) + + tgl.chat_add_user (peer, user) + tgl.chat_del_user (peer, user) + + tgl.add_contact (phone, first_name, last_name) + tgl.rename_contact (phone, first_name, last_name) + + tgl.msg_search (peer, text) + tgl.msg_global_search (text) + + tgl.mark_read (peer) + + tgl.set_profile_photo (file) + + tgl.create_secret_chat (user) + tgl.create_group_chat (peer, name) + + tgl.delete_msg (msg) + tgl.restore_msg (msg_id) + + tgl.status_online () + tgl.status_offline () + + tgl.send_location (peer, latitude, longitude) + +Additionally, the tgl.Peer object has the following direct methods: + peer.rename_chat(new_name) + peer.chat_set_photo(file) + peer.send_typing() + peer.send_typing_abort() + peer.send_msg(text) + peer.send_photo(file) + peer.send_video(file) + peer.send_document(file) + peer.send_text(file) + peer.info() + peer.history(limit, offset) + peer.add_user(peer) + peer.del_user(peer) + peer.search(text) + peer.mark_read() + peer.send_location(latitude, longitude) diff --git a/README.md b/README.md index f0f8c63..31e7179 100644 --- a/README.md +++ b/README.md @@ -24,30 +24,35 @@ Clone GitHub Repository git clone --recursive https://github.com/vysheng/tg.git && cd tg +### Python Support + +Python support is currently limited to Python 2.7 or Python 3.1+. Other versions may work but are not tested. + #### Linux and BSDs -Install libs: readline, openssl and (if you want to use config) libconfig, liblua and libjansson. -If you do not want to use them pass options --disable-libconfig, --disable-liblua and --disable-json respectively. +Install libs: readline, openssl and (if you want to use config) libconfig, liblua, python and libjansson. +If you do not want to use them pass options --disable-libconfig, --disable-liblua, --disable-python and --disable-json respectively. On Ubuntu/Debian use: - sudo apt-get install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev make + sudo apt-get install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make On gentoo: - sudo emerge -av sys-libs/readline dev-libs/libconfig dev-libs/openssl dev-lang/lua dev-libs/libevent dev-libs/jansson + sudo emerge -av sys-libs/readline dev-libs/libconfig dev-libs/openssl dev-lang/lua dev-libs/libevent dev-libs/jansson dev-lang/python On Fedora: sudo yum install lua-devel openssl-devel libconfig-devel readline-devel libevent-devel libjansson-devel + sudo yum install lua-devel openssl-devel libconfig-devel readline-devel libevent-devel python-devel On FreeBSD: - pkg install libconfig libexecinfo lua52 + pkg install libconfig libexecinfo lua52 python On OpenBSD: - pkg_add libconfig libexecinfo lua + pkg_add libconfig libexecinfo lua python Then, @@ -69,6 +74,7 @@ If using [Homebrew](http://brew.sh/): brew install libconfig brew install readline brew install lua + brew install python brew install libevent export CFLAGS="-I/usr/local/include -I/usr/local/Cellar/readline/6.3.8/include" export LDFLAGS="-L/usr/local/lib -L/usr/local/Cellar/readline/6.3.8/lib" @@ -81,6 +87,7 @@ If using [MacPorts](https://www.macports.org): sudo port install libconfig-hr sudo port install readline sudo port install lua51 + sudo port install python34 sudo port install libevent export CFLAGS="-I/usr/local/include -I/opt/local/include -I/opt/local/include/lua-5.1" export LDFLAGS="-L/usr/local/lib -L/opt/local/lib -L/opt/local/lib/lua-5.1" diff --git a/ax_python.m4 b/ax_python.m4 new file mode 100644 index 0000000..9702193 --- /dev/null +++ b/ax_python.m4 @@ -0,0 +1,109 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON +# +# DESCRIPTION +# +# This macro does a complete Python development environment check. +# +# It recurses through several python versions (from 2.1 to 2.6 in this +# version), looking for an executable. When it finds an executable, it +# looks to find the header files and library. +# +# It sets PYTHON_BIN to the name of the python executable, +# PYTHON_INCLUDE_DIR to the directory holding the header files, and +# PYTHON_LIB to the name of the Python library. +# +# This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG), +# PYTHON_INCLUDE_DIR and PYTHON_LIB. +# +# LICENSE +# +# Copyright (c) 2008 Michael Tindal +# +# 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 2 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, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 14 + +AC_DEFUN([AX_PYTHON], +[AC_MSG_CHECKING(for python build information) +AC_MSG_RESULT([]) +for python in python3.4 python3.3 python3.2 python3.1 python3 python2.7 python2.6 python2 python; do +AC_CHECK_PROGS(PYTHON_BIN, [$python]) +ax_python_bin=$PYTHON_BIN +if test x$ax_python_bin != x; then + AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no) + if test x$ax_python_lib == xno; then + AC_CHECK_LIB(${ax_python_bin}m, main, ax_python_lib=${ax_python_bin}m, ax_python_lib=no) + fi + if test x$ax_python_lib != xno; then + ax_python_header=`$ax_python_bin -c "from distutils.sysconfig import *; print(get_config_var('CONFINCLUDEPY'))"` + if test x$ax_python_header != x; then + break; + fi + fi +fi +unset ac_cv_prog_PYTHON_BIN +unset PYTHON_BIN +done +if test x$ax_python_bin = x; then + ax_python_bin=no +fi +if test x$ax_python_header = x; then + ax_python_header=no +fi +if test x$ax_python_lib = x; then + ax_python_lib=no +fi + +AC_MSG_RESULT([ results of the Python check:]) +AC_MSG_RESULT([ Binary: $ax_python_bin]) +AC_MSG_RESULT([ Library: $ax_python_lib]) +AC_MSG_RESULT([ Include Dir: $ax_python_header]) + + +PYTHON_FOUND=yes +if test x$ax_python_header != xno; then + PYTHON_INCLUDE_DIR=$ax_python_header + AC_SUBST(PYTHON_INCLUDE_DIR) +else + PYTHON_FOUND=no +fi + +if test x$ax_python_lib != xno; then + PYTHON_LIB=$ax_python_lib + AC_SUBST(PYTHON_LIB) +else + PYTHON_FOUND=no +fi +AC_SUBST(PYTHON_FOUND) + +])dnl diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..67cea4f --- /dev/null +++ b/bootstrap @@ -0,0 +1,6 @@ +#! /bin/sh + +aclocal +autoheader +automake --gnu --add-missing +autoconf diff --git a/config.h.in b/config.h.in index 38bec97..bb5f2d6 100644 --- a/config.h.in +++ b/config.h.in @@ -164,6 +164,9 @@ /* use lua */ #undef USE_LUA +/* use python */ +#undef USE_PYTHON + /* fixed for correct valgrind work */ #undef VALGRIND_FIXES diff --git a/configure b/configure index ea6dc0f..93723f5 100755 --- a/configure +++ b/configure @@ -622,6 +622,12 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS EXTRA_LIBS LIBOBJS +PYTHON_CFLAGS +PYTHON_LIBS +PYTHON_FOUND +PYTHON_LIB +PYTHON_INCLUDE_DIR +PYTHON_BIN LUA_LIB LUA_INCLUDE pkgluaexecdir @@ -694,6 +700,7 @@ with_zlib enable_libconfig enable_extf enable_liblua +enable_python enable_json with_progname enable_valgrind @@ -1322,6 +1329,7 @@ Optional Features: --enable-libconfig/--disable-libconfig --enable-extf/--disable-extf --enable-liblua/--disable-liblua +--enable-python/--disable-python --enable-json/--disable-json --enable-valgrind/--disable-valgrind @@ -2393,6 +2401,62 @@ ac_config_headers="$ac_config_headers config.h" + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON +# +# DESCRIPTION +# +# This macro does a complete Python development environment check. +# +# It recurses through several python versions (from 2.1 to 2.6 in this +# version), looking for an executable. When it finds an executable, it +# looks to find the header files and library. +# +# It sets PYTHON_BIN to the name of the python executable, +# PYTHON_INCLUDE_DIR to the directory holding the header files, and +# PYTHON_LIB to the name of the Python library. +# +# This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG), +# PYTHON_INCLUDE_DIR and PYTHON_LIB. +# +# LICENSE +# +# Copyright (c) 2008 Michael Tindal +# +# 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 2 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, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 14 # =========================================================================== @@ -5867,6 +5931,403 @@ fi fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python" >&5 +$as_echo_n "checking for python... " >&6; } +# Check whether --enable-python was given. +if test "${enable_python+set}" = set; then : + enableval=$enable_python; + if test "x$enableval" = "xno" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 +$as_echo "disabled" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 +$as_echo "enabled" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for python build information" >&5 +$as_echo_n "checking for python build information... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } +for python in python3.4 python3.3 python3.2 python3.1 python3 python2.7 python2.6 python2 python; do +for ac_prog in $python +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PYTHON_BIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PYTHON_BIN"; then + ac_cv_prog_PYTHON_BIN="$PYTHON_BIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PYTHON_BIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PYTHON_BIN=$ac_cv_prog_PYTHON_BIN +if test -n "$PYTHON_BIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_BIN" >&5 +$as_echo "$PYTHON_BIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PYTHON_BIN" && break +done + +ax_python_bin=$PYTHON_BIN +if test x$ax_python_bin != x; then + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_python_bin''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_python_bin" >&5 +$as_echo_n "checking for main in -l$ax_python_bin... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_python_bin $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + ax_python_lib=$ax_python_bin +else + ax_python_lib=no +fi + + if test x$ax_python_lib == xno; then + as_ac_Lib=`$as_echo "ac_cv_lib_${ax_python_bin}m''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l${ax_python_bin}m" >&5 +$as_echo_n "checking for main in -l${ax_python_bin}m... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l${ax_python_bin}m $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + ax_python_lib=${ax_python_bin}m +else + ax_python_lib=no +fi + + fi + if test x$ax_python_lib != xno; then + ax_python_header=`$ax_python_bin -c "from distutils.sysconfig import *; print(get_config_var('CONFINCLUDEPY'))"` + if test x$ax_python_header != x; then + break; + fi + fi +fi +unset ac_cv_prog_PYTHON_BIN +unset PYTHON_BIN +done +if test x$ax_python_bin = x; then + ax_python_bin=no +fi +if test x$ax_python_header = x; then + ax_python_header=no +fi +if test x$ax_python_lib = x; then + ax_python_lib=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: results of the Python check:" >&5 +$as_echo " results of the Python check:" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Binary: $ax_python_bin" >&5 +$as_echo " Binary: $ax_python_bin" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Library: $ax_python_lib" >&5 +$as_echo " Library: $ax_python_lib" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Include Dir: $ax_python_header" >&5 +$as_echo " Include Dir: $ax_python_header" >&6; } + + +PYTHON_FOUND=yes +if test x$ax_python_header != xno; then + PYTHON_INCLUDE_DIR=$ax_python_header + +else + PYTHON_FOUND=no +fi + +if test x$ax_python_lib != xno; then + PYTHON_LIB=$ax_python_lib + +else + PYTHON_FOUND=no +fi + + + + + if test $PYTHON_FOUND = no ; then + as_fn_error $? "No supported python lib version found. Try --disable-python" "$LINENO" 5 + else + + + EXTRA_LIBS="${EXTRA_LIBS} -l${PYTHON_LIB}" + CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE_DIR}" + +$as_echo "#define USE_PYTHON 1" >>confdefs.h + + fi + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 +$as_echo "enabled" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for python build information" >&5 +$as_echo_n "checking for python build information... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } +for python in python3.4 python3.3 python3.2 python3.1 python3 python2.7 python2.6 python2 python; do +for ac_prog in $python +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PYTHON_BIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PYTHON_BIN"; then + ac_cv_prog_PYTHON_BIN="$PYTHON_BIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PYTHON_BIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PYTHON_BIN=$ac_cv_prog_PYTHON_BIN +if test -n "$PYTHON_BIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_BIN" >&5 +$as_echo "$PYTHON_BIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PYTHON_BIN" && break +done + +ax_python_bin=$PYTHON_BIN +if test x$ax_python_bin != x; then + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_python_bin''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_python_bin" >&5 +$as_echo_n "checking for main in -l$ax_python_bin... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_python_bin $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + ax_python_lib=$ax_python_bin +else + ax_python_lib=no +fi + + if test x$ax_python_lib == xno; then + as_ac_Lib=`$as_echo "ac_cv_lib_${ax_python_bin}m''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l${ax_python_bin}m" >&5 +$as_echo_n "checking for main in -l${ax_python_bin}m... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l${ax_python_bin}m $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + ax_python_lib=${ax_python_bin}m +else + ax_python_lib=no +fi + + fi + if test x$ax_python_lib != xno; then + ax_python_header=`$ax_python_bin -c "from distutils.sysconfig import *; print(get_config_var('CONFINCLUDEPY'))"` + if test x$ax_python_header != x; then + break; + fi + fi +fi +unset ac_cv_prog_PYTHON_BIN +unset PYTHON_BIN +done +if test x$ax_python_bin = x; then + ax_python_bin=no +fi +if test x$ax_python_header = x; then + ax_python_header=no +fi +if test x$ax_python_lib = x; then + ax_python_lib=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: results of the Python check:" >&5 +$as_echo " results of the Python check:" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Binary: $ax_python_bin" >&5 +$as_echo " Binary: $ax_python_bin" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Library: $ax_python_lib" >&5 +$as_echo " Library: $ax_python_lib" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Include Dir: $ax_python_header" >&5 +$as_echo " Include Dir: $ax_python_header" >&6; } + + +PYTHON_FOUND=yes +if test x$ax_python_header != xno; then + PYTHON_INCLUDE_DIR=$ax_python_header + +else + PYTHON_FOUND=no +fi + +if test x$ax_python_lib != xno; then + PYTHON_LIB=$ax_python_lib + +else + PYTHON_FOUND=no +fi + + + + + if test $PYTHON_FOUND = no ; then + as_fn_error $? "No supported python lib version found. Try --disable-python" "$LINENO" 5 + else + + + EXTRA_LIBS="${EXTRA_LIBS} -l${PYTHON_LIB}" + CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE_DIR}" + +$as_echo "#define USE_PYTHON 1" >>confdefs.h + + fi + +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libjansson" >&5 $as_echo_n "checking for libjansson... " >&6; } # Check whether --enable-json was given. diff --git a/configure.ac b/configure.ac index 3e33cc2..a2c09e5 100644 --- a/configure.ac +++ b/configure.ac @@ -4,6 +4,7 @@ AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADERS([config.h]) m4_include([ax_lua.m4]) +m4_include([ax_python.m4]) m4_include([m4_ax_check_openssl.m4]) m4_include([m4_ax_check_zlib.m4]) m4_include([m4-ax_gcc_builtin.m4]) @@ -94,6 +95,44 @@ AC_ARG_ENABLE(liblua,[--enable-liblua/--disable-liblua], ]) ]) +AC_MSG_CHECKING([for python]) +AC_ARG_ENABLE(python,[--enable-python/--disable-python], + [ + if test "x$enableval" = "xno" ; then + AC_MSG_RESULT([disabled]) + else + AC_MSG_RESULT([enabled]) + + AX_PYTHON() + AC_SUBST([PYTHON_FOUND]) + if test $PYTHON_FOUND = no ; then + AC_MSG_ERROR([No supported python lib version found. Try --disable-python]) + else + AC_SUBST([PYTHON_LIBS]) + AC_SUBST([PYTHON_CFLAGS]) + EXTRA_LIBS="${EXTRA_LIBS} -l${PYTHON_LIB}" + CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE_DIR}" + AC_DEFINE(USE_PYTHON,1,[use python]) + fi + fi + ],[ + AC_MSG_RESULT([enabled]) + + AX_PYTHON() + AC_SUBST([PYTHON_FOUND]) + if test $PYTHON_FOUND = no ; then + AC_MSG_ERROR([No supported python lib version found. Try --disable-python]) + else + AC_SUBST([PYTHON_LIBS]) + AC_SUBST([PYTHON_CFLAGS]) + EXTRA_LIBS="${EXTRA_LIBS} -l${PYTHON_LIB}" + CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE_DIR}" + AC_DEFINE(USE_PYTHON,1,[use python]) + fi + ]) + + + AC_MSG_CHECKING([for libjansson]) AC_ARG_ENABLE(json,[--enable-json/--disable-json], [ diff --git a/gentoo/net-im/telegram-cli/telegram-cli-9999.ebuild b/gentoo/net-im/telegram-cli/telegram-cli-9999.ebuild index c6ec5b3..de50269 100644 --- a/gentoo/net-im/telegram-cli/telegram-cli-9999.ebuild +++ b/gentoo/net-im/telegram-cli/telegram-cli-9999.ebuild @@ -20,7 +20,9 @@ DEPEND="sys-libs/zlib dev-libs/libconfig dev-libs/openssl dev-libs/libevent - lua? ( dev-lang/lua )" + lua? ( dev-lang/lua ) + json? ( dev-lib/jansson ) + python? ( dev-lang/python )" src_unpack() { git-2_src_unpack @@ -30,6 +32,8 @@ src_unpack() { src_configure() { econf $(use_enable lua liblua ) + econf $(use_enable python python ) + econf $(use_enable json json ) } src_install() { diff --git a/interface.c b/interface.c index 5256acf..6de300d 100644 --- a/interface.c +++ b/interface.c @@ -21,7 +21,13 @@ #include "config.h" #endif -#define _GNU_SOURCE +#ifdef USE_PYTHON +# include "python-tg.h" +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include #include @@ -59,6 +65,7 @@ # include "lua-tg.h" #endif + //#include "mtproto-common.h" #include @@ -1288,6 +1295,7 @@ extern char *downloads_directory; extern char *config_directory; extern char *binlog_file_name; extern char *lua_file; +extern char *python_file; extern struct event *term_ev; void do_clear (struct command *command, int arg_num, struct arg args[], struct in_ev *ev) { @@ -1302,6 +1310,7 @@ void do_clear (struct command *command, int arg_num, struct arg args[], struct i free (config_directory); free (binlog_file_name); free (lua_file); + free (python_file); clear_history (); event_free (term_ev); struct event_base *ev_base = TLS->ev_base; @@ -2332,6 +2341,9 @@ void print_message_gw (struct tgl_state *TLSR, struct tgl_message *M) { #ifdef USE_LUA lua_new_msg (M); #endif + #ifdef USE_PYTHON + py_new_msg (M); + #endif if (!binlog_read) { return; } if (tgl_get_peer_type (M->to_id) == TGL_PEER_ENCR_CHAT) { write_secret_chat_file (); @@ -2361,6 +2373,9 @@ void our_id_gw (struct tgl_state *TLSR, int id) { #ifdef USE_LUA lua_our_id (id); #endif + #ifdef USE_PYTHON + py_our_id (id); + #endif } void print_peer_updates (struct in_ev *ev, int flags) { @@ -2426,7 +2441,10 @@ void user_update_gw (struct tgl_state *TLSR, struct tgl_user *U, unsigned flags) #ifdef USE_LUA lua_user_update (U, flags); #endif - + #ifdef USE_PYTHON + py_user_update (U, flags); + #endif + if (disable_output && !notify_ev) { return; } if (!binlog_read) { return; } struct in_ev *ev = notify_ev; @@ -2457,7 +2475,10 @@ void chat_update_gw (struct tgl_state *TLSR, struct tgl_chat *U, unsigned flags) #ifdef USE_LUA lua_chat_update (U, flags); #endif - + #ifdef USE_PYTHON + py_chat_update (U, flags); + #endif + if (disable_output && !notify_ev) { return; } if (!binlog_read) { return; } struct in_ev *ev = notify_ev; @@ -2488,7 +2509,12 @@ void secret_chat_update_gw (struct tgl_state *TLSR, struct tgl_secret_chat *U, u #ifdef USE_LUA lua_secret_chat_update (U, flags); #endif + #ifdef USE_PYTHON + py_secret_chat_update (U, flags); + #endif + + if ((flags & TGL_UPDATE_WORKING) || (flags & TGL_UPDATE_DELETED)) { write_secret_chat_file (); } diff --git a/loop.c b/loop.c index 1bbe620..050537b 100644 --- a/loop.c +++ b/loop.c @@ -21,7 +21,13 @@ #include "config.h" #endif +#if USE_PYTHON +#include "python-tg.h" +#endif + +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #define READLINE_CALLBACKS #include @@ -56,7 +62,10 @@ #include "interface.h" #include "telegram.h" #include "loop.h" +#if USE_LUA #include "lua-tg.h" +#endif + #include #include #include @@ -215,6 +224,10 @@ void net_loop (void) { lua_do_all (); #endif + #ifdef USE_PYTHON + py_do_all (); + #endif + if (safe_quit && !TLS->active_queries) { printf ("All done. Exit\n"); do_halt (0); @@ -655,6 +668,10 @@ void on_started (struct tgl_state *TLS) { lua_diff_end (); #endif + #ifdef USE_PYTHON + py_diff_end (); + #endif + if (start_command) { safe_quit = 1; while (*start_command) { @@ -710,6 +727,10 @@ int loop (void) { lua_binlog_end (); #endif + #ifdef USE_PYTHON + py_binlog_end (); + #endif + if (sfd >= 0) { struct event *ev = event_new (TLS->ev_base, sfd, EV_READ | EV_PERSIST, accept_incoming, 0); event_add (ev, 0); diff --git a/main.c b/main.c index 888eece..9452d27 100644 --- a/main.c +++ b/main.c @@ -21,6 +21,10 @@ #include "config.h" #endif +#ifdef USE_PYTHON +# include "python-tg.h" +#endif + #include #include #include @@ -75,6 +79,7 @@ # include "lua-tg.h" #endif + #include #define PROGNAME "telegram-cli" @@ -107,6 +112,7 @@ char *downloads_directory; char *config_directory; char *binlog_file_name; char *lua_file; +char *python_file; int binlog_enabled; extern int log_level; int sync_from_start; @@ -381,6 +387,10 @@ void parse_config (void) { parse_config_val (&conf, &lua_file, "lua_script", 0, config_directory); } + if (!python_file) { + parse_config_val (&conf, &python_file, "python_script", 0, config_directory); + } + strcpy (buf + l, "binlog_enabled"); config_lookup_bool (&conf, buf, &binlog_enabled); @@ -484,6 +494,26 @@ void usage (void) { #ifdef USE_JSON printf (" --json prints answers and values in json format\n"); #endif + #ifdef USE_PYTHON + printf (" -Z python script file\n"); + #endif + #ifdef USE_PYTHON + printf (" -Z python script file\n"); + #endif + printf (" -W send dialog_list query and wait for answer before reading input\n"); + printf (" -C disable color output\n"); + printf (" -R disable readline\n"); + printf (" -d daemon mode\n"); + printf (" -L log file name\n"); + printf (" -U change uid after start\n"); + printf (" -G change gid after start\n"); + printf (" -D disable output\n"); + printf (" -P port to listen for input commands\n"); + printf (" -S unix socket to create\n"); + printf (" -e make commands end exit\n"); + printf (" -I use user and chat IDs in updates instead of names\n"); + printf (" -6 use ipv6 (may be unstable)\n"); + exit (1); } @@ -643,8 +673,12 @@ void args_parse (int argc, char **argv) { #endif #ifdef USE_LUA "s:" +#endif +#ifdef USE_PYTHON + "Z:" #endif , long_options, NULL + )) != -1) { switch (opt) { case 1000: @@ -700,6 +734,11 @@ void args_parse (int argc, char **argv) { case 'W': wait_dialog_list = 1; break; +#ifdef USE_PYTHON + case 'Z': + python_file = strdup (optarg); + break; +#endif case 'C': disable_colors ++; break; @@ -922,6 +961,9 @@ int main (int argc, char **argv) { "This is free software, and you are welcome to redistribute it\n" "under certain conditions; type `show_license' for details.\n" "Telegram-cli uses libtgl version " TGL_VERSION "\n" +#ifdef USE_PYTHON + "Telegram-cli uses libpython version " PY_VERSION "\n" +#endif ); } running_for_first_time (); @@ -942,6 +984,12 @@ int main (int argc, char **argv) { lua_init (lua_file); } #endif + #ifdef USE_PYTHON + if (python_file) { + py_init (python_file); + } + #endif + inner_main (); diff --git a/python-tg.c b/python-tg.c new file mode 100644 index 0000000..a045bda --- /dev/null +++ b/python-tg.c @@ -0,0 +1,1253 @@ +/* + This file is part of telegram-cli. + + Telegram-cli 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 2 of the License, or + (at your option) any later version. + + Telegram-cli 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 telegram-cli. If not, see . + + Copyright Vitaly Valtman 2013-2015 + Copyright Vincent Castellano 2015 +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_PYTHON +#include "python-tg.h" + +#include +#include +#include + +#include +#include "bytesobject.h" + +// Python 2/3 compat macros +#if PY_MAJOR_VERSION >= 3 + #define MOD_ERROR_VAL NULL + #define MOD_SUCCESS_VAL(val) val + #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) + #define MOD_DEF(ob, name, doc, methods) \ + static struct PyModuleDef moduledef = { \ + PyModuleDef_HEAD_INIT, name, doc, -1, methods, NULL, NULL, NULL, NULL,}; \ + ob = PyModule_Create(&moduledef); + #define PyInt_FromLong PyLong_FromLong +#else + #define MOD_ERROR_VAL + #define MOD_SUCCESS_VAL(val) + #define MOD_INIT(name) void init##name(void) + #define MOD_DEF(ob, name, doc, methods) \ + ob = Py_InitModule3(name, methods, doc); +#endif + +#define TGL_PYTHON_CALLBACK(name, func) \ + PyObject *set##func(PyObject *dummy, PyObject *args) { \ + PyObject *result = NULL; \ + PyObject *temp; \ + if (PyArg_ParseTuple(args, "O:set_##name", &temp)) { \ + if (!PyCallable_Check(temp)) { \ + PyErr_SetString(PyExc_TypeError, "parameter must be callable");\ + return NULL;\ + }\ + Py_XINCREF(temp);\ + Py_XDECREF(func);\ + func = temp;\ + Py_INCREF(Py_None);\ + result = Py_None;\ + }\ + return result;\ + } + + +// Python Imports +#include "datetime.h" + +// Custom Types +#include "python-types.h" + + +extern PyTypeObject tgl_PeerType; +extern PyTypeObject tgl_MsgType; + +//#include "interface.h" +//#include "auto/constants.h" +#include +#include "interface.h" + +#include +extern int verbosity; +extern struct tgl_state *TLS; + + +static int python_loaded; + +// TGL Python Exceptions +PyObject *TglError; +PyObject *PeerError; +PyObject *MsgError; + + +// Python update function callables +PyObject *_py_binlog_end; +PyObject *_py_diff_end; +PyObject *_py_our_id; +PyObject *_py_new_msg; +PyObject *_py_secret_chat_update; +PyObject *_py_user_update; +PyObject *_py_chat_update; + +PyObject* get_peer (tgl_peer_id_t id, tgl_peer_t *P); + +void py_add_string_field (PyObject* dict, char *name, const char *value) { + assert (PyDict_Check(dict)); + assert (name && strlen (name)); + if (!value || !strlen (value)) { return; } + PyObject *str = PyUnicode_FromString(value); + + if(PyUnicode_Check(str)) + PyDict_SetItemString (dict, name, str); +} + +void py_add_string_field_arr (PyObject* list, int num, const char *value) { + assert(PyList_Check(list)); + if (!value || !strlen (value)) { return; } + if(num >= 0) + PyList_SetItem (list, num, PyUnicode_FromString (value)); + else // Append + PyList_Append (list, PyUnicode_FromString (value)); +} + +void py_add_num_field (PyObject* dict, const char *name, double value) { + assert (PyDict_Check(dict)); + assert (name && strlen (name)); + PyDict_SetItemString (dict, name, PyFloat_FromDouble(value)); +} + +PyObject* get_tgl_peer_type (int x) { + PyObject *type; + + switch (x) { + case TGL_PEER_USER: + type = PyUnicode_FromString("user"); + break; + case TGL_PEER_CHAT: + type = PyUnicode_FromString("chat"); + break; + case TGL_PEER_ENCR_CHAT: + type = PyUnicode_FromString("encr_chat"); + break; + default: + assert (0); + } + + return type; +} + +PyObject* get_update_types (unsigned flags) { + PyObject* types; + types = PyList_New(0); + if(types == NULL) + assert(0); // TODO handle python exception + + if (flags & TGL_UPDATE_CREATED) { + py_add_string_field_arr(types, -1, "created"); + } + if (flags & TGL_UPDATE_DELETED) { + py_add_string_field_arr(types, -1, "deleted"); + } + if (flags & TGL_UPDATE_PHONE) { + py_add_string_field_arr(types, -1, "phone"); + } + if (flags & TGL_UPDATE_CONTACT) { + py_add_string_field_arr(types, -1, "contact"); + } + if (flags & TGL_UPDATE_PHOTO) { + py_add_string_field_arr(types, -1, "photo"); + } + if (flags & TGL_UPDATE_BLOCKED) { + py_add_string_field_arr(types, -1, "blocked"); + } + if (flags & TGL_UPDATE_REAL_NAME) { + py_add_string_field_arr(types, -1, "real_name"); + } + if (flags & TGL_UPDATE_NAME) { + py_add_string_field_arr(types, -1, "name"); + } + if (flags & TGL_UPDATE_REQUESTED) { + py_add_string_field_arr(types, -1, "requested"); + } + if (flags & TGL_UPDATE_WORKING) { + py_add_string_field_arr(types, -1, "working"); + } + if (flags & TGL_UPDATE_FLAGS) { + py_add_string_field_arr(types, -1, "flags"); + } + if (flags & TGL_UPDATE_TITLE) { + py_add_string_field_arr(types, -1, "title"); + } + if (flags & TGL_UPDATE_ADMIN) { + py_add_string_field_arr(types, -1, "admin"); + } + if (flags & TGL_UPDATE_MEMBERS) { + py_add_string_field_arr(types, -1, "members"); + } + if (flags & TGL_UPDATE_ACCESS_HASH) { + py_add_string_field_arr(types, -1, "access_hash"); + } + if (flags & TGL_UPDATE_USERNAME) { + py_add_string_field_arr(types, -1, "username"); + } + return types; +} + +PyObject* get_peer (tgl_peer_id_t id, tgl_peer_t *P) { + PyObject *peer; + + peer = tgl_Peer_FromTglPeer(P); + return peer; +} + +PyObject* get_message (struct tgl_message *M) { + assert (M); + PyObject *msg; + + msg = tgl_Msg_FromTglMsg(M); + return msg; +} + +void py_binlog_end (void) { + if (!python_loaded) { return; } + + PyObject *arglist, *result; + + if(_py_binlog_end == NULL) { + logprintf("Callback not set for on_binlog_end"); + return; + } + + arglist = Py_BuildValue("()"); + result = PyEval_CallObject(_py_binlog_end, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + +void py_diff_end (void) { + if (!python_loaded) { return; } + + PyObject *arglist, *result; + + if(_py_diff_end == NULL) { + logprintf("Callback not set for on_diff_end"); + return; + } + + arglist = Py_BuildValue("()"); + result = PyEval_CallObject(_py_diff_end, arglist); + Py_DECREF(arglist); + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + +void py_our_id (int id) { + if (!python_loaded) { return; } + + PyObject *arglist, *result; + + if(_py_our_id == NULL) { + logprintf("Callback not set for on_our_id"); + return; + } + + arglist = Py_BuildValue("(i)", id); + result = PyEval_CallObject(_py_our_id, arglist); + Py_DECREF(arglist); + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + +void py_new_msg (struct tgl_message *M) { + if (!python_loaded) { return; } + PyObject *msg; + PyObject *arglist, *result; + + if(_py_new_msg == NULL) { + logprintf("Callback not set for on_new_msg"); + return; + } + + msg = get_message (M); + + arglist = Py_BuildValue("(O)", msg); + result = PyEval_CallObject(_py_new_msg, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + +void py_secret_chat_update (struct tgl_secret_chat *C, unsigned flags) { + if (!python_loaded) { return; } + PyObject *peer, *types; + PyObject *arglist, *result; + + if(_py_secret_chat_update == NULL) { + logprintf("Callback not set for on_secret_chat_update"); + return; + } + + peer = get_peer (C->id, (void *)C); + types = get_update_types (flags); + + arglist = Py_BuildValue("(OO)", peer, types); + result = PyEval_CallObject(_py_secret_chat_update, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + + +void py_user_update (struct tgl_user *U, unsigned flags) { + if (!python_loaded) { return; } + PyObject *peer, *types; + PyObject *arglist, *result; + + if(_py_user_update == NULL) { + logprintf("Callback not set for on_user_update"); + return; + } + + peer = get_peer (U->id, (void *)U); + types = get_update_types (flags); + + arglist = Py_BuildValue("(OO)", peer, types); + result = PyEval_CallObject(_py_user_update, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + +void py_chat_update (struct tgl_chat *C, unsigned flags) { + if (!python_loaded) { return; } + + PyObject *peer, *types; + PyObject *arglist, *result; + + if(_py_chat_update == NULL) { + logprintf("Callback not set for on_chat_update"); + return; + } + + peer = get_peer (C->id, (void *)C); + types = get_update_types (flags); + + arglist = Py_BuildValue("(OO)", peer, types); + result = PyEval_CallObject(_py_chat_update, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + else if(PyUnicode_Check(result)) + logprintf ("python: %s\n", PyBytes_AsString(PyUnicode_AsASCIIString(result))); + + Py_XDECREF(result); +} + +////extern tgl_peer_t *Peers[]; +////extern int peer_num; +// +#define MAX_PY_COMMANDS 1000 +void *py_ptr[MAX_PY_COMMANDS]; +static int pos; +// +//static inline tgl_peer_t *get_peer (const char *s) { +// return tgl_peer_get_by_name (TLS, s); +//} + +enum py_query_type { + pq_contact_list, + pq_dialog_list, + pq_msg, + pq_send_typing, + pq_send_typing_abort, + pq_rename_chat, + pq_send_photo, + pq_chat_set_photo, + pq_set_profile_photo, + pq_set_profile_name, + pq_send_video, + pq_send_text, + pq_fwd, + pq_fwd_media, + pq_load_photo, + pq_load_video_thumb, + pq_load_video, + pq_chat_info, + pq_user_info, + pq_history, + pq_chat_add_user, + pq_chat_del_user, + pq_add_contact, + pq_del_contact, + pq_rename_contact, + pq_search, + pq_global_search, + pq_mark_read, + pq_create_secret_chat, + pq_create_group_chat, + pq_send_audio, + pq_send_document, + pq_send_file, + pq_load_audio, + pq_load_document, + pq_load_document_thumb, + pq_delete_msg, + pq_restore_msg, + pq_accept_secret_chat, + pq_send_contact, + pq_status_online, + pq_status_offline, + pq_send_location, + pq_extf +}; + +void py_empty_cb (struct tgl_state *TLSR, void *cb_extra, int success) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + arglist = Py_BuildValue("(O)", success ? Py_True : Py_False); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_contact_list_cb (struct tgl_state *TLSR, void *cb_extra, int success, int num, struct tgl_user **UL) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *peers = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + peers = PyList_New(0); + if (success) { + int i; + for (i = 0; i < num; i++) { + PyList_Append(peers, get_peer (UL[i]->id, (void *)UL[i])); + } + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, peers); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_dialog_list_cb (struct tgl_state *TLSR, void *cb_extra, int success, int num, tgl_peer_id_t peers[], int msgs[], int unread[]) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *dialog_list = NULL; + PyObject *dialog = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + dialog_list = PyList_New(0); + if (success) { + int i; + for (i = 0; i < num; i++) { + dialog = PyDict_New(); + PyDict_SetItemString(dialog, "peer", get_peer(peers[i], tgl_peer_get (TLS, peers[i]))); + + struct tgl_message *M = tgl_message_get (TLS, msgs[i]); + if (M && (M->flags & TGLMF_CREATED)) { + PyDict_SetItemString(dialog, "message", get_message(M)); + } + PyDict_SetItemString(dialog, "unread", unread[i] ? Py_True : Py_False); + + PyList_Append(dialog_list, dialog); + } + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, dialog_list); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_msg_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_message *M) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *msg = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + if (success && M && (M->flags & TGLMF_CREATED)) { + msg = get_message(M); + } else { + Py_INCREF(Py_None); + msg = Py_None; + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, msg); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_msg_list_cb (struct tgl_state *TLSR, void *cb_extra, int success, int num, struct tgl_message *M[]) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *msgs = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + msgs = PyList_New(0); + if (success) { + int i; + for (i = 0; i < num; i++) { + PyList_Append(msgs, get_message (M[i])); + } + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, msgs); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_file_cb (struct tgl_state *TLSR, void *cb_extra, int success, const char *file_name) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *filename = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + if(success) + filename = PyUnicode_FromString(file_name); + else { + Py_INCREF(Py_None); + filename = Py_None; + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, filename); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_chat_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_chat *C) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *peer = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + if (success) { + peer = get_peer(C->id, (void *)C); + } else { + Py_INCREF(Py_None); + peer = Py_None; + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, peer); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_secret_chat_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_secret_chat *C) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *peer = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + if (success) { + peer = get_peer(C->id, (void *)C); + } else { + Py_INCREF(Py_None); + peer = Py_None; + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, peer); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_user_cb (struct tgl_state *TLSR, void *cb_extra, int success, struct tgl_user *C) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *peer = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + if (success) { + peer = get_peer(C->id, (void *)C); + } else { + Py_INCREF(Py_None); + peer = Py_None; + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, peer); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +void py_str_cb (struct tgl_state *TLSR, void *cb_extra, int success, const char *data) { + assert (TLSR == TLS); + PyObject *callable = cb_extra; + PyObject *arglist = NULL; + PyObject *str = NULL; + PyObject *result = NULL; + + if(PyCallable_Check(callable)) { + if(success) + str = PyUnicode_FromString(data); + else { + Py_INCREF(Py_None); + str = Py_None; + } + + arglist = Py_BuildValue("(OO)", success ? Py_True : Py_False, str); + result = PyEval_CallObject(callable, arglist); + Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } + + Py_XDECREF(callable); +} + +#define PY_PEER_ID(x) (tgl_peer_id_t)((tgl_Peer*)x)->peer->id + +void py_do_all (void) { + int p = 0; + while (p < pos) { + assert (p + 2 <= pos); + + enum py_query_type f = (long)py_ptr[p ++]; + PyObject *args = (PyObject *)py_ptr[p ++]; + + const char *str, *str1, *str2, *str3; + + Py_ssize_t i; + tgl_user_id_t *ids; + + int len, len1, len2, len3; + int limit, offset; + long msg_id = 0; + PyObject *pyObj1 = NULL; + PyObject *pyObj2 = NULL; + PyObject *cb_extra = NULL; + + PyObject *peer = NULL; + PyObject *peer1 = NULL; + + switch (f) { + case pq_contact_list: + if(PyArg_ParseTuple(args, "|O", &cb_extra)) + tgl_do_update_contact_list (TLS, py_contact_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_dialog_list: + if(PyArg_ParseTuple(args, "|O", &cb_extra)) + tgl_do_get_dialog_list (TLS, 100, 0, py_dialog_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_msg: + if(PyArg_ParseTuple(args, "O!s#|O", &tgl_PeerType, &peer, &str, &len, &cb_extra)) + tgl_do_send_message (TLS, PY_PEER_ID(peer), str, len, 0, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_typing: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_send_typing (TLS, PY_PEER_ID(peer), tgl_typing_typing, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_typing_abort: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_send_typing (TLS, PY_PEER_ID(peer), tgl_typing_cancel, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_rename_chat: + if(PyArg_ParseTuple(args, "O!s#|O", &tgl_PeerType, &peer, &str, &len, &cb_extra)) + tgl_do_rename_chat (TLS, PY_PEER_ID(peer), str, len, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_photo: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_send_document (TLS, PY_PEER_ID(peer), str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_PHOTO, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_video: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_send_document (TLS, PY_PEER_ID(peer), str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_VIDEO, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_audio: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_send_document (TLS, PY_PEER_ID(peer), str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_AUDIO, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_document: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_send_document (TLS, PY_PEER_ID(peer), str, NULL, 0, 0, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_file: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_send_document (TLS, PY_PEER_ID(peer), str, NULL, 0, TGL_SEND_MSG_FLAG_DOCUMENT_AUTO, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_send_text: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_send_text (TLS, PY_PEER_ID(peer), str, 0, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_chat_set_photo: + if(PyArg_ParseTuple(args, "O!s|O", &tgl_PeerType, &peer, &str, &cb_extra)) + tgl_do_set_chat_photo (TLS, PY_PEER_ID(peer), str, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; +/* case pq_load_photo: + case pq_load_video: + case pq_load_audio: + case pq_load_document: + M = py_ptr[p + 1]; + if (!M || (M->media.type != tgl_message_media_photo && M->media.type != tgl_message_media_photo_encr && M->media.type != tgl_message_media_document && M->media.type != tgl_message_media_document_encr)) { + py_file_cb (TLS, py_ptr[p], 0, 0); + } else { + , limit, offse, limit, offsettif (M->media.type == tgl_message_media_photo) { + tgl_do_load_photo (TLS, &M->media.photo, py_file_cb, py_ptr[p]); + } else if (M->media.type == tgl_message_media_document) { + tgl_do_load_document (TLS, &M->media.document, py_file_cb, py_ptr[p]); + } else { + tgl_do_load_encr_document (TLS, &M->media.encr_document, py_file_cb, py_ptr[p]); + } + } + break; + case pq_load_video_thumb: + case pq_load_document_thumb: + M = py_ptr[p + 1]; + if (!M || (M->media.type != tgl_message_media_document)) { + py_file_cb (TLS, py_ptr[p], 0, 0); + } else { + tgl_do_load_document_thumb (TLS, &M->media.document, py_file_cb, py_ptr[p]); + } + break; +*/ + case pq_fwd: + if(PyArg_ParseTuple(args, "O!l|O", &tgl_PeerType, &peer, &msg_id, &cb_extra)) + tgl_do_forward_message (TLS, PY_PEER_ID(peer), msg_id, 0, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_fwd_media: + if(PyArg_ParseTuple(args, "O!l|O", &tgl_PeerType, &peer, &msg_id, &cb_extra)) + tgl_do_forward_media (TLS, PY_PEER_ID(peer), msg_id, 0, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_chat_info: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_get_chat_info (TLS, PY_PEER_ID(peer), 0, py_chat_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_user_info: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_get_user_info (TLS, PY_PEER_ID(peer), 0, py_user_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_history: + if(PyArg_ParseTuple(args, "O!ii|O", &tgl_PeerType, &peer, &offset, &limit, &cb_extra)) + tgl_do_get_history (TLS, PY_PEER_ID(peer), offset, limit, 0, py_msg_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_chat_add_user: + if(PyArg_ParseTuple(args, "O!O!|O", &tgl_PeerType, &peer, &tgl_PeerType, &peer1, &cb_extra)) + tgl_do_add_user_to_chat (TLS, PY_PEER_ID(peer), PY_PEER_ID(peer1), 100, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_chat_del_user: + if(PyArg_ParseTuple(args, "O!O!|O", &tgl_PeerType, &peer, &tgl_PeerType, &peer1, &cb_extra)) + tgl_do_del_user_from_chat (TLS, PY_PEER_ID(peer), PY_PEER_ID(peer1), py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_add_contact: + if(PyArg_ParseTuple(args, "s#s#s#|O", &str1, &len1, &str2, &len2, &str3, &len3, &cb_extra)) + tgl_do_add_contact (TLS, str1, len1, str2, len2, str3, len3, 0, py_contact_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_del_contact: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_del_contact (TLS, PY_PEER_ID(peer), py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_rename_contact: + if(PyArg_ParseTuple(args, "s#s#s#|O", &str1, &len1, &str2, &len2, &str3, &len3, &cb_extra)) + tgl_do_add_contact (TLS, str1, len1, str2, len2, str3, len3, 1, py_contact_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_search: + if(PyArg_ParseTuple(args, "O!s#|O", &tgl_PeerType, &peer, &str, &len, &cb_extra)) + tgl_do_msg_search (TLS, PY_PEER_ID(peer), 0, 0, 40, 0, str, len, py_msg_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_global_search: + if(PyArg_ParseTuple(args, "s#|O", &str, &len, &cb_extra)) + tgl_do_msg_search (TLS, tgl_set_peer_id (TGL_PEER_UNKNOWN, 0), 0, 0, 40, 0, str, len, py_msg_list_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_mark_read: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_mark_read (TLS, PY_PEER_ID(peer), py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_set_profile_photo: + if(PyArg_ParseTuple(args, "s|O", &str, &cb_extra)) + tgl_do_set_profile_photo (TLS, str, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_set_profile_name: + if(PyArg_ParseTuple(args, "s#s#|O", &str1, &len1, &str2, &len2, &cb_extra)) + tgl_do_set_profile_name (TLS, str1, len1, str2, len2, py_user_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_create_secret_chat: + if(PyArg_ParseTuple(args, "O!|O", &tgl_PeerType, &peer, &cb_extra)) + tgl_do_create_secret_chat (TLS, PY_PEER_ID(peer), py_secret_chat_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_create_group_chat: + if(PyArg_ParseTuple(args, "O!s#|O", &PyList_Type, &pyObj1, &str, &len, &cb_extra)) { + if(PyList_GET_SIZE(pyObj1) > 2) { + ids = (tgl_user_id_t *)malloc(PyList_GET_SIZE(pyObj1) * sizeof(tgl_user_id_t)); + for(i = 0; i < PyList_GET_SIZE(pyObj1); i++) { + peer = PyList_GetItem(pyObj1, i); + *(ids+i) = PY_PEER_ID(peer); + } + tgl_do_create_group_chat (TLS, PyList_GET_SIZE(pyObj1), ids, str, len, py_empty_cb, cb_extra); + + tfree(ids, PyList_GET_SIZE(pyObj1) * sizeof(tgl_user_id_t)); + } else { + logprintf("create_group_chat: Argument 1 must be a list of at least 3 peers"); + } + } + Py_XDECREF(pyObj1); + break; + case pq_delete_msg: + case pq_restore_msg: + if(PyArg_ParseTuple(args, "l|O", &msg_id, &cb_extra)) + tgl_do_delete_msg (TLS, msg_id, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; +/* + case pq_accept_secret_chat: + tgl_do_accept_encr_chat_request (TLS, py_ptr[p + 1], py_secret_chat_cb, py_ptr[p]); + break; +*/ + case pq_send_contact: + if(PyArg_ParseTuple(args, "O!s#s#s#|O", &tgl_PeerType, &peer, &str1, &len1, &str2, &len2, + &str3, &len3, &cb_extra)) + tgl_do_send_contact (TLS, PY_PEER_ID(peer), str1, len1, str2, len2, str3, len3, 0, py_msg_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_status_online: + if(PyArg_ParseTuple(args, "|O", &cb_extra)) + tgl_do_update_status (TLS, 1, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_status_offline: + if(PyArg_ParseTuple(args, "|O", &cb_extra)) + tgl_do_update_status (TLS, 0, py_empty_cb, cb_extra); + else + PyErr_Print(); + break; + case pq_extf: + if(PyArg_ParseTuple(args, "s#|O", &str, &len, &cb_extra)) + tgl_do_send_extf (TLS, str, len, py_str_cb, &cb_extra); + else + PyErr_Print(); + break; + case pq_send_location: + if(PyArg_ParseTuple(args, "O!O!O!|O", &tgl_PeerType, &peer, &PyFloat_Type, &pyObj1, &PyFloat_Type, &pyObj2, &cb_extra)){ + tgl_do_send_location (TLS, PY_PEER_ID(peer), + PyFloat_AsDouble(pyObj1), PyFloat_AsDouble(pyObj2), 0, py_msg_cb, cb_extra); + Py_XDECREF(pyObj1); + Py_XDECREF(pyObj2); + } else + PyErr_Print(); + break; + default: + assert (0); + } + + // Increment reference on cb_extra as it is passed on to the callback to use + Py_XINCREF(cb_extra); + + // Clean up any arg variables we could have used. + //Py_XDECREF(args); // TODO: this is going negative ref and causing segfaults + Py_XDECREF(peer); + Py_XDECREF(peer1); + + } + pos = 0; +} + +PyObject* push_py_func(enum py_query_type type, PyObject *args) { + assert(pos + 2 < MAX_PY_COMMANDS); + + py_ptr[pos ++] = (void *)(long)type; + py_ptr[pos ++] = (void *)args; + + Py_INCREF(args); + Py_RETURN_TRUE; +} + +// Register functions to push commands on the queue +PyObject* py_contact_list(PyObject *self, PyObject *args) { return push_py_func(pq_contact_list, args); } +PyObject* py_dialog_list(PyObject *self, PyObject *args) { return push_py_func(pq_dialog_list, args); } +PyObject* py_rename_chat(PyObject *self, PyObject *args) { return push_py_func(pq_rename_chat, args); } +PyObject* py_send_msg(PyObject *self, PyObject *args) { return push_py_func(pq_msg, args); } +PyObject* py_send_typing(PyObject *self, PyObject *args) { return push_py_func(pq_send_typing, args); } +PyObject* py_send_typing_abort(PyObject *self, PyObject *args) { return push_py_func(pq_send_typing_abort, args); } +PyObject* py_send_photo(PyObject *self, PyObject *args) { return push_py_func(pq_send_photo, args); } +PyObject* py_send_video(PyObject *self, PyObject *args) { return push_py_func(pq_send_video, args); } +PyObject* py_send_audio(PyObject *self, PyObject *args) { return push_py_func(pq_send_audio, args); } +PyObject* py_send_document(PyObject *self, PyObject *args) { return push_py_func(pq_send_document, args); } +PyObject* py_send_file(PyObject *self, PyObject *args) { return push_py_func(pq_send_file, args); } +PyObject* py_send_text(PyObject *self, PyObject *args) { return push_py_func(pq_send_text, args); } +PyObject* py_chat_set_photo(PyObject *self, PyObject *args) { return push_py_func(pq_chat_set_photo, args); } +PyObject* py_load_photo(PyObject *self, PyObject *args) { return push_py_func(pq_load_photo, args); } +PyObject* py_load_video(PyObject *self, PyObject *args) { return push_py_func(pq_load_video, args); } +PyObject* py_load_video_thumb(PyObject *self, PyObject *args) { return push_py_func(pq_load_video_thumb, args); } +PyObject* py_load_audio(PyObject *self, PyObject *args) { return push_py_func(pq_load_audio, args); } +PyObject* py_load_document(PyObject *self, PyObject *args) { return push_py_func(pq_load_document, args); } +PyObject* py_load_document_thumb(PyObject *self, PyObject *args) { return push_py_func(pq_load_document_thumb, args); } +PyObject* py_fwd(PyObject *self, PyObject *args) { return push_py_func(pq_fwd, args); } +PyObject* py_fwd_media(PyObject *self, PyObject *args) { return push_py_func(pq_fwd_media, args); } +PyObject* py_chat_info(PyObject *self, PyObject *args) { return push_py_func(pq_chat_info, args); } +PyObject* py_user_info(PyObject *self, PyObject *args) { return push_py_func(pq_chat_info, args); } +PyObject* py_history(PyObject *self, PyObject *args) { return push_py_func(pq_history, args); } +PyObject* py_chat_add_user(PyObject *self, PyObject *args) { return push_py_func(pq_chat_add_user, args); } +PyObject* py_chat_del_user(PyObject *self, PyObject *args) { return push_py_func(pq_chat_del_user, args); } +PyObject* py_add_contact(PyObject *self, PyObject *args) { return push_py_func(pq_add_contact, args); } +PyObject* py_del_contact(PyObject *self, PyObject *args) { return push_py_func(pq_del_contact, args); } +PyObject* py_rename_contact(PyObject *self, PyObject *args) { return push_py_func(pq_rename_contact, args); } +PyObject* py_search(PyObject *self, PyObject *args) { return push_py_func(pq_search, args); } +PyObject* py_global_search(PyObject *self, PyObject *args) { return push_py_func(pq_global_search, args); } +PyObject* py_mark_read(PyObject *self, PyObject *args) { return push_py_func(pq_mark_read, args); } +PyObject* py_set_profile_photo(PyObject *self, PyObject *args) { return push_py_func(pq_set_profile_photo, args); } +PyObject* py_set_profile_name(PyObject *self, PyObject *args) { return push_py_func(pq_set_profile_name, args); } +PyObject* py_create_secret_chat(PyObject *self, PyObject *args) { return push_py_func(pq_create_secret_chat, args); } +PyObject* py_create_group_chat(PyObject *self, PyObject *args) { return push_py_func(pq_create_group_chat, args); } +PyObject* py_delete_msg(PyObject *self, PyObject *args) { return push_py_func(pq_delete_msg, args); } +PyObject* py_restore_msg(PyObject *self, PyObject *args) { return push_py_func(pq_restore_msg, args); } +PyObject* py_accept_secret_chat(PyObject *self, PyObject *args) { return push_py_func(pq_accept_secret_chat, args); } +PyObject* py_send_contact(PyObject *self, PyObject *args) { return push_py_func(pq_send_contact, args); } +PyObject* py_status_online(PyObject *self, PyObject *args) { return push_py_func(pq_status_online, args); } +PyObject* py_status_offline(PyObject *self, PyObject *args) { return push_py_func(pq_status_offline, args); } +PyObject* py_send_location(PyObject *self, PyObject *args) { return push_py_func(pq_send_location, args); } +PyObject* py_extf(PyObject *self, PyObject *args) { return push_py_func(pq_extf, args); } + + +// Store callables for python functions +TGL_PYTHON_CALLBACK("on_binlog_replay_end", _py_binlog_end); +TGL_PYTHON_CALLBACK("on_get_difference_end", _py_diff_end); +TGL_PYTHON_CALLBACK("on_our_id", _py_our_id); +TGL_PYTHON_CALLBACK("on_msg_receive", _py_new_msg); +TGL_PYTHON_CALLBACK("on_secret_chat_update", _py_secret_chat_update); +TGL_PYTHON_CALLBACK("on_user_update", _py_user_update); +TGL_PYTHON_CALLBACK("on_chat_update", _py_chat_update); + +static PyMethodDef py_tgl_methods[] = { + {"get_contact_list", py_contact_list, METH_VARARGS, "retrieve contact list"}, + {"get_dialog_list", py_dialog_list, METH_VARARGS, ""}, + {"rename_chat", py_rename_chat, METH_VARARGS, ""}, + {"send_msg", py_send_msg, METH_VARARGS, "send message to user or chat"}, + {"send_typing", py_send_typing, METH_VARARGS, ""}, + {"send_typing_abort", py_send_typing_abort, METH_VARARGS, ""}, + {"send_photo", py_send_photo, METH_VARARGS, ""}, + {"send_video", py_send_video, METH_VARARGS, ""}, + {"send_audio", py_send_audio, METH_VARARGS, ""}, + {"send_document", py_send_document, METH_VARARGS, ""}, + {"send_file", py_send_file, METH_VARARGS, ""}, + {"send_text", py_send_text, METH_VARARGS, ""}, + {"chat_set_photo", py_chat_set_photo, METH_VARARGS, ""}, + {"load_photo", py_load_photo, METH_VARARGS, ""}, + {"load_video", py_load_video, METH_VARARGS, ""}, + {"load_video_thumb", py_load_video_thumb, METH_VARARGS, ""}, + {"load_audio", py_load_audio, METH_VARARGS, ""}, + {"load_document", py_load_document, METH_VARARGS, ""}, + {"load_document_thumb", py_load_document_thumb, METH_VARARGS, ""}, + {"fwd_msg", py_fwd, METH_VARARGS, ""}, + {"fwd_media", py_fwd_media, METH_VARARGS, ""}, + {"chat_info", py_chat_info, METH_VARARGS, ""}, + {"user_info", py_user_info, METH_VARARGS, ""}, + {"get_history", py_history, METH_VARARGS, ""}, + {"chat_add_user", py_chat_add_user, METH_VARARGS, ""}, + {"chat_del_user", py_chat_del_user, METH_VARARGS, ""}, + {"add_contact", py_add_contact, METH_VARARGS, ""}, + {"del_contact", py_del_contact, METH_VARARGS, ""}, + {"rename_contact", py_rename_contact, METH_VARARGS, ""}, + {"msg_search", py_search, METH_VARARGS, ""}, + {"msg_global_search", py_global_search, METH_VARARGS, ""}, + {"mark_read", py_mark_read, METH_VARARGS, ""}, + {"set_profile_photo", py_set_profile_photo, METH_VARARGS, ""}, + {"set_profile_name", py_set_profile_name, METH_VARARGS, ""}, + {"create_secret_chat", py_create_secret_chat, METH_VARARGS, ""}, + {"create_group_chat", py_create_group_chat, METH_VARARGS, ""}, + {"delete_msg", py_delete_msg, METH_VARARGS, ""}, + {"restore_msg", py_restore_msg, METH_VARARGS, ""}, + {"accept_secret_chat", py_accept_secret_chat, METH_VARARGS, ""}, + {"send_contact", py_send_contact, METH_VARARGS, ""}, + {"status_online", py_status_online, METH_VARARGS, ""}, + {"status_offline", py_status_offline, METH_VARARGS, ""}, + {"send_location", py_send_location, METH_VARARGS, ""}, + {"ext_function", py_extf, METH_VARARGS, ""}, + {"set_on_binlog_replay_end", set_py_binlog_end, METH_VARARGS, ""}, + {"set_on_get_difference_end", set_py_diff_end, METH_VARARGS, ""}, + {"set_on_our_id", set_py_our_id, METH_VARARGS, ""}, + {"set_on_msg_receive", set_py_new_msg, METH_VARARGS, ""}, + {"set_on_secret_chat_update", set_py_secret_chat_update, METH_VARARGS, ""}, + {"set_on_user_update", set_py_user_update, METH_VARARGS, ""}, + {"set_on_chat_update", set_py_chat_update, METH_VARARGS, ""}, + { NULL, NULL, 0, NULL } +}; + +MOD_INIT(tgl) +{ + PyObject *m; + + MOD_DEF(m, "tgl", NULL, py_tgl_methods) + + if (m == NULL) + return MOD_ERROR_VAL; + + if (PyType_Ready(&tgl_PeerType) < 0) + return MOD_ERROR_VAL; + + Py_INCREF(&tgl_PeerType); + PyModule_AddObject(m, "Peer", (PyObject *)&tgl_PeerType); + + if (PyType_Ready(&tgl_MsgType) < 0) + return MOD_ERROR_VAL; + + Py_INCREF(&tgl_MsgType); + PyModule_AddObject(m, "Msg", (PyObject *)&tgl_MsgType); + + TglError = PyErr_NewException("tgl.Error", NULL, NULL); + Py_INCREF(TglError); + PyModule_AddObject(m, "TglError", TglError); + + PeerError = PyErr_NewException("tgl.PeerError", NULL, NULL); + Py_INCREF(PeerError); + PyModule_AddObject(m, "PeerError", PeerError); + + MsgError = PyErr_NewException("tgl.MsgError", NULL, NULL); + Py_INCREF(MsgError); + PyModule_AddObject(m, "MsgError", MsgError); + + return MOD_SUCCESS_VAL(m); +} + +/* +extern int safe_quit; +static int safe_quit_from_py() { + Py_Finalize(); + safe_quit = 1; + return 1; +} +*/ + +void py_init (const char *file) { + if (!file) { return; } + python_loaded = 0; + + PyObject *pModule; + + // Get a copy of the filename for dirname/basename, which may modify the string, and break const correctness + char filename[1024]; + strncpy(filename, file, 1024); + + + +#if PY_MAJOR_VERSION >= 3 + PyImport_AppendInittab("tgl", &PyInit_tgl); + Py_Initialize(); +#else + Py_Initialize(); + inittgl(); +#endif + + + PyObject* sysPath = PySys_GetObject((char*)"path"); + PyList_Append(sysPath, PyUnicode_FromString(dirname(filename))); + + // Recopy the string in, since dirname modified it. + strncpy(filename, file, 1024); + + // remove .py extension from file, if any + char* dot = strrchr(filename, '.'); + if (dot && strcmp(dot, ".py") == 0) + *dot = 0; + pModule = PyImport_Import(PyUnicode_FromString(basename(filename))); + + if(pModule == NULL || PyErr_Occurred()) { // Error loading script + logprintf("Failed to load python script\n"); + PyErr_Print(); + exit(1); + } + + + python_loaded = 1; + PyDateTime_IMPORT; + logprintf("Python Initialized\n"); +} + +#endif diff --git a/python-tg.h b/python-tg.h new file mode 100644 index 0000000..bc724ea --- /dev/null +++ b/python-tg.h @@ -0,0 +1,89 @@ +/* + This file is part of telegram-cli. + + Telegram-cli 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 2 of the License, or + (at your option) any later version. + + Telegram-cli 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 telegram-cli. If not, see . + + Copyright Vitaly Valtman 2013-2015 + Copyright Vincent Castellano 2015 +*/ +#ifndef __PYTHON_TG_H__ +#define __PYTHON_TG_H__ + +#include +#include +#include + +// Python functions +void py_init (const char *file); +void py_new_msg (struct tgl_message *M); +void py_our_id (int id); +void py_secret_chat_update (struct tgl_secret_chat *U, unsigned flags); +void py_user_update (struct tgl_user *U, unsigned flags); +void py_chat_update (struct tgl_chat *C, unsigned flags); +void py_binlog_end (void); +void py_diff_end (void); +void py_do_all (void); + +// Binding functions +PyObject* py_contact_list(PyObject *self, PyObject *args); +PyObject* py_dialog_list(PyObject *self, PyObject *args); +PyObject* py_rename_chat(PyObject *self, PyObject *args); +PyObject* py_send_msg(PyObject *self, PyObject *args); +PyObject* py_send_typing(PyObject *self, PyObject *args); +PyObject* py_send_typing_abort(PyObject *self, PyObject *args); +PyObject* py_send_photo(PyObject *self, PyObject *args); +PyObject* py_send_video(PyObject *self, PyObject *args); +PyObject* py_send_audio(PyObject *self, PyObject *args); +PyObject* py_send_document(PyObject *self, PyObject *args); +PyObject* py_send_file(PyObject *self, PyObject *args); +PyObject* py_send_text(PyObject *self, PyObject *args); +PyObject* py_chat_set_photo(PyObject *self, PyObject *args); +PyObject* py_load_photo(PyObject *self, PyObject *args); +PyObject* py_load_video(PyObject *self, PyObject *args); +PyObject* py_load_video_thumb(PyObject *self, PyObject *args); +PyObject* py_load_audio(PyObject *self, PyObject *args); +PyObject* py_load_document(PyObject *self, PyObject *args); +PyObject* py_load_document_thumb(PyObject *self, PyObject *args); +PyObject* py_fwd(PyObject *self, PyObject *args); +PyObject* py_fwd_media(PyObject *self, PyObject *args); +PyObject* py_chat_info(PyObject *self, PyObject *args); +PyObject* py_user_info(PyObject *self, PyObject *args); +PyObject* py_history(PyObject *self, PyObject *args); +PyObject* py_chat_add_user(PyObject *self, PyObject *args); +PyObject* py_chat_del_user(PyObject *self, PyObject *args); +PyObject* py_add_contact(PyObject *self, PyObject *args); +PyObject* py_del_contact(PyObject *self, PyObject *args); +PyObject* py_rename_contact(PyObject *self, PyObject *args); +PyObject* py_search(PyObject *self, PyObject *args); +PyObject* py_global_search(PyObject *self, PyObject *args); +PyObject* py_mark_read(PyObject *self, PyObject *args); +PyObject* py_set_profile_photo(PyObject *self, PyObject *args); +PyObject* py_set_profile_name(PyObject *self, PyObject *args); +PyObject* py_create_secret_chat(PyObject *self, PyObject *args); +PyObject* py_create_group_chat(PyObject *self, PyObject *args); +PyObject* py_delete_msg(PyObject *self, PyObject *args); +PyObject* py_restore_msg(PyObject *self, PyObject *args); +PyObject* py_accept_secret_chat(PyObject *self, PyObject *args); +PyObject* py_send_contact(PyObject *self, PyObject *args); +PyObject* py_status_online(PyObject *self, PyObject *args); +PyObject* py_status_offline(PyObject *self, PyObject *args); +PyObject* py_send_location(PyObject *self, PyObject *args); +PyObject* py_extf(PyObject *self, PyObject *args); + + +// Util Functions +void py_add_string_field (PyObject* dict, char *name, const char *value); +void py_add_string_field_arr (PyObject* list, int num, const char *value); +void py_add_num_field (PyObject* dict, const char *name, double value); +#endif diff --git a/python-types.c b/python-types.c new file mode 100644 index 0000000..c073cdb --- /dev/null +++ b/python-types.c @@ -0,0 +1,1422 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_PYTHON +#include +#include +#include +#include +#include +#include +#include "structmember.h" + +// Python Imports +#include "datetime.h" + +#include "python-types.h" +#include "python-tg.h" + +extern struct tgl_state *TLS; + +// TGL Python Exceptions +extern PyObject *TglError; +extern PyObject *PeerError; +extern PyObject *MsgError; + +// Forward type declarations +PyTypeObject tgl_PeerType; + +// Utility functions +PyObject* get_datetime(long datetime) +{ + return PyDateTime_FromTimestamp(Py_BuildValue("(O)", PyLong_FromLong(datetime))); +} + + +// +// tgl_peer_t wrapper +// +static void +tgl_Peer_dealloc(tgl_Peer* self) +{ + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +tgl_Peer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + tgl_Peer *self; + + PyDateTime_IMPORT; + self = (tgl_Peer *)type->tp_alloc(type, 0); + return (PyObject *)self; +} + +static int +tgl_Peer_init(tgl_Peer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"type", "id", NULL}; + tgl_peer_id_t peer_id; + if(!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, + &peer_id.type, + &peer_id.id)) + { + PyErr_Format(PeerError, "Peer must specify type and id"); + return -1; + } + self->peer = tgl_peer_get(TLS, peer_id); + if(self->peer == NULL) + return -1; + + return 0; +} + +static PyObject * +tgl_Peer_getname (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + ret = PyUnicode_FromString(self->peer->user.print_name); + break; + case TGL_PEER_CHAT: + ret = PyUnicode_FromString(self->peer->chat.print_title); + break; + case TGL_PEER_ENCR_CHAT: + ret = PyUnicode_FromString(self->peer->encr_chat.print_name); + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + + +static PyObject * +tgl_Peer_getuser_id (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + ret = PyLong_FromLong(self->peer->id.id); + break; + case TGL_PEER_CHAT: + PyErr_SetString(PeerError, "peer.type_name == 'chat' has no user_id"); + Py_RETURN_NONE; + + break; + case TGL_PEER_ENCR_CHAT: + ret = PyLong_FromLong(self->peer->encr_chat.user_id); + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getuser_list (tgl_Peer *self, void *closure) +{ + PyObject *ret; + int i; + struct tgl_chat_user *user_list; + + switch(self->peer->id.type) { + case TGL_PEER_CHAT: + ret = PyList_New(0); + for(i = 0; i < self->peer->chat.user_list_size; i++) { + // TODO: Sort tgl_user objects, maybe offline mode is enoug? + user_list = self->peer->chat.user_list + i; + PyList_Append(ret, PyLong_FromLong(user_list->user_id)); + } + break; + case TGL_PEER_ENCR_CHAT: + case TGL_PEER_USER: + PyErr_SetString(PeerError, "Only peer.type_name == 'chat' has user_list"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getuser_status(tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + ret = PyDict_New(); + PyDict_SetItemString(ret, "online", self->peer->user.status.online? Py_True : Py_False); + PyDict_SetItemString(ret, "when", get_datetime(self->peer->user.status.when)); + + break; + case TGL_PEER_CHAT: + case TGL_PEER_ENCR_CHAT: + PyErr_SetString(PeerError, "Only peer.type_name == 'user' has user_status"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getphone (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + if(self->peer->user.phone) + ret = PyUnicode_FromString(self->peer->user.phone); + else + Py_RETURN_NONE; + break; + case TGL_PEER_CHAT: + case TGL_PEER_ENCR_CHAT: + PyErr_SetString(PeerError, "Only peer.type_name == 'user' has phone"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getusername (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + if(self->peer->user.username) + ret = PyUnicode_FromString(self->peer->user.username); + else + Py_RETURN_NONE; + break; + case TGL_PEER_CHAT: + case TGL_PEER_ENCR_CHAT: + PyErr_SetString(PeerError, "Only peer.type_name == 'user' has username"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getfirst_name (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + if(self->peer->user.first_name) + ret = PyUnicode_FromString(self->peer->user.first_name); + else + Py_RETURN_NONE; + break; + case TGL_PEER_CHAT: + case TGL_PEER_ENCR_CHAT: + PyErr_SetString(PeerError, "Only peer.type_name == 'user' has first_name"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getlast_name (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + if(self->peer->user.last_name) + ret = PyUnicode_FromString(self->peer->user.last_name); + else + Py_RETURN_NONE; + break; + case TGL_PEER_CHAT: + case TGL_PEER_ENCR_CHAT: + PyErr_SetString(PeerError, "Only peer.type_name == 'user' has last_name"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_getuser (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_ENCR_CHAT: + ret = tgl_Peer_FromTglPeer(tgl_peer_get(TLS, TGL_MK_USER (self->peer->encr_chat.user_id))); + break; + case TGL_PEER_USER: + ret = (PyObject*)self; + break; + case TGL_PEER_CHAT: + PyErr_SetString(PeerError, "Only peer.type_name == 'chat' does not have user"); + Py_RETURN_NONE; + break; + default: + PyErr_SetString(PeerError, "peer.type_name not supported!"); + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_gettype_name(tgl_Peer* self) +{ + PyObject *name; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + name = PyUnicode_FromString("user"); + break; + case TGL_PEER_CHAT: + name = PyUnicode_FromString("chat"); + break; + case TGL_PEER_ENCR_CHAT: + name = PyUnicode_FromString("secret_chat"); + break; + default: + name = PyUnicode_FromString("unknown"); + } + return name; +} + +static PyObject * +tgl_Peer_getid (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + ret = PyLong_FromLong(self->peer->id.id); + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Peer_gettype (tgl_Peer *self, void *closure) +{ + PyObject *ret; + + ret = PyLong_FromLong(self->peer->id.type); + + Py_XINCREF(ret); + return ret; +} + +static PyGetSetDef tgl_Peer_getseters[] = { + {"id", (getter)tgl_Peer_getid, NULL, "", NULL}, + {"type", (getter)tgl_Peer_gettype, NULL, "", NULL}, + {"type_name", (getter)tgl_Peer_gettype_name, NULL, "", NULL}, + {"name", (getter)tgl_Peer_getname, NULL, "", NULL}, + {"user_id", (getter)tgl_Peer_getuser_id, NULL, "", NULL}, + {"user", (getter)tgl_Peer_getuser, NULL, "", NULL}, + {"user_list", (getter)tgl_Peer_getuser_list, NULL, "", NULL}, + {"user_status", (getter)tgl_Peer_getuser_status, NULL, "", NULL}, + {"phone", (getter)tgl_Peer_getphone, NULL, "", NULL}, + {"username", (getter)tgl_Peer_getusername, NULL, "", NULL}, + {"first_name", (getter)tgl_Peer_getfirst_name, NULL, "", NULL}, + {"last_name", (getter)tgl_Peer_getlast_name, NULL, "", NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef tgl_Peer_members[] = { + {NULL} /* Sentinel */ +}; + +static PyObject * +tgl_Peer_send_msg (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"message", "callback", NULL}; + + char *message; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &message, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, message, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, message); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_msg(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_typing (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"callback", NULL}; + + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OO", (PyObject*) self, callback); + else + api_call = Py_BuildValue("O", (PyObject*) self); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_typing(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_typing_abort (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"callback", NULL}; + + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OO", (PyObject*) self, callback); + else + api_call = Py_BuildValue("O", (PyObject*) self); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_typing_abort(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_rename_chat (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"title", "callback", NULL}; + + char * title; + PyObject *callback = NULL; + + if(self->peer->id.type != TGL_PEER_CHAT) { + PyErr_SetString(PeerError, "Only a chat peer can be renamed"); + Py_XINCREF(Py_False); + return Py_False; + } + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &title, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, title, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, title); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_rename_chat(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_photo (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char *filename; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_photo(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_video (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char *filename; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_video(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_audio (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char *filename; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_audio(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_document (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char *filename; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_document(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_file (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char *filename; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_file(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_text (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char *filename; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_text(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_chat_set_photo (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "callback", NULL}; + + char * filename; + PyObject *callback = NULL; + + if(self->peer->id.type != TGL_PEER_CHAT) { + PyErr_SetString(PeerError, "Only a chat peer can have a chat photo set."); + Py_XINCREF(Py_False); + return Py_False; + } + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &filename, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OsO", (PyObject*) self, filename, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, filename); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_chat_set_photo(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_chat_add_user (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"peer", "callback", NULL}; + + PyObject *peer; + PyObject *callback = NULL; + + if(self->peer->id.type != TGL_PEER_CHAT) { + PyErr_SetString(PeerError, "Only a chat peer can have a user added."); + Py_XINCREF(Py_False); + return Py_False; + } + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &tgl_PeerType, &peer, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OOO", (PyObject*) self, peer, callback); + else + api_call = Py_BuildValue("OO", (PyObject*) self, peer); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_chat_add_user(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_chat_del_user (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"peer", "callback", NULL}; + + PyObject *peer; + PyObject *callback = NULL; + + if(self->peer->id.type != TGL_PEER_CHAT) { + PyErr_SetString(PeerError, "Only a chat peer can have a user deleted."); + Py_XINCREF(Py_False); + return Py_False; + } + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &tgl_PeerType, &peer, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OOO", (PyObject*) self, peer, callback); + else + api_call = Py_BuildValue("OO", (PyObject*) self, peer); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_chat_del_user(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_history (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"offset", "limit", "callback", NULL}; + + int offset, limit; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "ii|O", kwlist, &offset, &limit, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OiiO", (PyObject*) self, offset, limit, callback); + else + api_call = Py_BuildValue("Oii", (PyObject*) self, offset, limit); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_history(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_info (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"callback", NULL}; + + PyObject *callback = NULL; + + if(self->peer->id.type == TGL_PEER_ENCR_CHAT) { + PyErr_SetString(PeerError, "Secret chats currently have no info."); + Py_XINCREF(Py_False); + return Py_False; + } + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OO", (PyObject*) self, callback); + else + api_call = Py_None; + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + if(self->peer->id.type == TGL_PEER_USER) + return py_user_info(Py_None, api_call); + else + return py_chat_info(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_send_contact (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"phone", "first_name", "last_name", "callback", NULL}; + + char *phone, *first_name, *last_name; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "sss|O", kwlist, &phone, &first_name, &last_name, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("Osss", (PyObject*) self, phone, first_name, last_name, callback); + else + api_call = Py_BuildValue("Os", (PyObject*) self, phone, first_name, last_name); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_contact(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + + +static PyObject * +tgl_Peer_send_location (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"latitude", "longitude", "callback", NULL}; + + PyObject *latitude, *longitude; + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O", kwlist, &PyFloat_Type, + &latitude, &PyFloat_Type, &longitude, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OOOO", (PyObject*) self, latitude, longitude, callback); + else + api_call = Py_BuildValue("OOO", (PyObject*) self, latitude, longitude); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_send_location(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyObject * +tgl_Peer_mark_read (tgl_Peer *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"callback", NULL}; + + PyObject *callback = NULL; + + if(PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &callback)) { + PyObject *api_call; + + if(callback) + api_call = Py_BuildValue("OO", (PyObject*) self, callback); + else + api_call = Py_BuildValue("O", (PyObject*) self); + + Py_INCREF(Py_None); + Py_XINCREF(api_call); + + return py_mark_read(Py_None, api_call); + } else { + PyErr_Print(); + Py_XINCREF(Py_False); + return Py_False; + } + +} + +static PyMethodDef tgl_Peer_methods[] = { + {"send_msg", (PyCFunction)tgl_Peer_send_msg, METH_VARARGS | METH_KEYWORDS, + "Send a message to peer object"}, + {"send_typing", (PyCFunction)tgl_Peer_send_typing, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_typing_abort", (PyCFunction)tgl_Peer_send_typing_abort, METH_VARARGS | METH_KEYWORDS, ""}, + {"rename_chat", (PyCFunction)tgl_Peer_rename_chat, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_photo", (PyCFunction)tgl_Peer_send_photo, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_video", (PyCFunction)tgl_Peer_send_video, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_audio", (PyCFunction)tgl_Peer_send_audio, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_file", (PyCFunction)tgl_Peer_send_file, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_document", (PyCFunction)tgl_Peer_send_document, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_text", (PyCFunction)tgl_Peer_send_text, METH_VARARGS | METH_KEYWORDS, ""}, + {"chat_set_photo", (PyCFunction)tgl_Peer_chat_set_photo, METH_VARARGS | METH_KEYWORDS, ""}, + {"info", (PyCFunction)tgl_Peer_info, METH_VARARGS | METH_KEYWORDS, ""}, + {"history", (PyCFunction)tgl_Peer_history, METH_VARARGS | METH_KEYWORDS, ""}, + {"chat_add_user", (PyCFunction)tgl_Peer_chat_add_user, METH_VARARGS | METH_KEYWORDS, ""}, + {"chat_del_user", (PyCFunction)tgl_Peer_chat_del_user, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_contact", (PyCFunction)tgl_Peer_send_contact, METH_VARARGS | METH_KEYWORDS, ""}, + {"send_location", (PyCFunction)tgl_Peer_send_location, METH_VARARGS | METH_KEYWORDS, ""}, + {"mark_read", (PyCFunction)tgl_Peer_mark_read, METH_VARARGS | METH_KEYWORDS, ""}, + {NULL} /* Sentinel */ +}; + + +static PyObject * +tgl_Peer_repr(tgl_Peer *self) +{ + PyObject *ret; + + switch(self->peer->id.type) { + case TGL_PEER_USER: + ret = PyUnicode_FromFormat("", + self->peer->id.id, + PyObject_GetAttrString((PyObject*)self, "username"), + PyObject_GetAttrString((PyObject*)self, "name"), + PyObject_GetAttrString((PyObject*)self, "first_name"), + PyObject_GetAttrString((PyObject*)self, "last_name"), + PyObject_GetAttrString((PyObject*)self, "phone") + ); + break; + case TGL_PEER_CHAT: + ret = PyUnicode_FromFormat("", + self->peer->id.id, self->peer->chat.print_title); + break; + case TGL_PEER_ENCR_CHAT: + ret = PyUnicode_FromFormat("", + self->peer->id.id, self->peer->encr_chat.print_name, + PyObject_GetAttrString((PyObject*)self, "user")); + break; + default: + ret = PyUnicode_FromFormat(""); + } + + return ret; +} + + +PyTypeObject tgl_PeerType = { + PyVarObject_HEAD_INIT(NULL, 0) + "tgl.Peer", /* tp_name */ + sizeof(tgl_Peer), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)tgl_Peer_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)tgl_Peer_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "tgl Peer", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + tgl_Peer_methods, /* tp_methods */ + tgl_Peer_members, /* tp_members */ + tgl_Peer_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)tgl_Peer_init, /* tp_init */ + 0, /* tp_alloc */ + tgl_Peer_new, /* tp_new */ +}; + + +PyObject * +tgl_Peer_FromTglPeer(tgl_peer_t *peer) { + tgl_Peer *self = (tgl_Peer *) tgl_Peer_new((PyTypeObject *)&tgl_PeerType, Py_None, Py_None); + + self->peer = peer; + return (PyObject *) self; +} + +// +// struct tgl_message wrapper +// + +static void +tgl_Msg_dealloc(tgl_Msg* self) +{ + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +tgl_Msg_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + tgl_Msg *self; + + PyDateTime_IMPORT; + self = (tgl_Msg *)type->tp_alloc(type, 0); + return (PyObject *)self; +} + +static int +tgl_Msg_init(tgl_Msg *self, PyObject *args, PyObject *kwds) +{ + PyErr_SetString(MsgError, "You cannot instantiate a tgl.Msg object, only the API can send them."); + return -1; +} + +static PyObject * +tgl_Msg_getid (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + ret = PyLong_FromLong(self->msg->id); + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getflags (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + ret = PyLong_FromLong(self->msg->flags); + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getmention (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + ret = ((self->msg->flags & TGLMF_MENTION) ? Py_True : Py_False); + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getout (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + ret = ((self->msg->flags & TGLMF_OUT) ? Py_True : Py_False); + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getunread (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + ret = ((self->msg->flags & TGLMF_UNREAD) ? Py_True : Py_False); + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getservice (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + ret = ((self->msg->flags & TGLMF_SERVICE) ? Py_True : Py_False); + + Py_XINCREF(ret); + return ret; +} + + +static PyObject * +tgl_Msg_getsrc (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(tgl_get_peer_type (self->msg->from_id)) { + tgl_peer_t *peer = tgl_peer_get (TLS, self->msg->from_id); + if(peer) + ret = tgl_Peer_FromTglPeer(peer); + else { + PyErr_SetString(PeerError, "Cannot Retrieve Peer. Internal tgl error"); + Py_RETURN_NONE; + } + + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getdest (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(tgl_get_peer_type (self->msg->to_id)) { + tgl_peer_t *peer = tgl_peer_get (TLS, self->msg->to_id); + if(peer) + ret = tgl_Peer_FromTglPeer(peer); + else { + PyErr_SetString(PeerError, "Cannot Retrieve Peer. Internal tgl error"); + Py_RETURN_NONE; + } + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_gettext (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(self->msg->message_len && self->msg->message && !(self->msg->flags & TGLMF_SERVICE)) { + ret = PyUnicode_FromStringAndSize(self->msg->message, self->msg->message_len); + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getmedia (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + // TODO probably want a custom class for media, but it's not too important right now. + if(self->msg->media.type && self->msg->media.type != tgl_message_media_none && !(self->msg->flags & TGLMF_SERVICE)) { + + ret = PyDict_New(); + switch (self->msg->media.type) { + case tgl_message_media_photo: + py_add_string_field (ret, "type", "photo"); + py_add_string_field (ret, "caption", self->msg->media.caption); + break; + case tgl_message_media_document: + case tgl_message_media_document_encr: + py_add_string_field (ret, "type", "document"); + break; + case tgl_message_media_unsupported: + py_add_string_field (ret, "type", "unsupported"); + break; + case tgl_message_media_geo: + py_add_string_field (ret, "type", "geo"); + py_add_num_field (ret, "longitude", self->msg->media.geo.longitude); + py_add_num_field (ret, "latitude", self->msg->media.geo.latitude); + break; + case tgl_message_media_contact: + py_add_string_field (ret, "type", "contact"); + py_add_string_field (ret, "phone", self->msg->media.phone); + py_add_string_field (ret, "first_name", self->msg->media.first_name); + py_add_string_field (ret, "last_name", self->msg->media.last_name); + py_add_num_field (ret, "user_id", self->msg->media.user_id); + break; + case tgl_message_media_webpage: + py_add_string_field (ret, "type", "webpage"); + py_add_string_field (ret, "type", "webpage"); + py_add_string_field (ret, "url", self->msg->media.webpage->url); + py_add_string_field (ret, "title", self->msg->media.webpage->title); + py_add_string_field (ret, "description", self->msg->media.webpage->description); + py_add_string_field (ret, "author", self->msg->media.webpage->author); + break; + case tgl_message_media_venue: + py_add_string_field (ret, "type", "venue"); + py_add_num_field (ret, "longitude", self->msg->media.venue.geo.longitude); + py_add_num_field (ret, "latitude", self->msg->media.venue.geo.latitude); + py_add_string_field (ret, "title", self->msg->media.venue.title); + py_add_string_field (ret, "address", self->msg->media.venue.address); + py_add_string_field (ret, "provider", self->msg->media.venue.provider); + py_add_string_field (ret, "venue_id", self->msg->media.venue.venue_id); + break; + default: + py_add_string_field (ret, "type", "unknown"); + } + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getdate (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(self->msg->date) { + ret = get_datetime(self->msg->date); + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getfwd_src (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(tgl_get_peer_type (self->msg->fwd_from_id)) { + tgl_peer_t *peer = tgl_peer_get (TLS, self->msg->fwd_from_id); + if(peer) + ret = tgl_Peer_FromTglPeer(peer); + else { + PyErr_SetString(PeerError, "Cannot Retrieve Peer. Internal tgl error"); + Py_RETURN_NONE; + } + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getfwd_date (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(tgl_get_peer_type (self->msg->fwd_from_id)) { + ret = get_datetime(self->msg->fwd_date); + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getreply (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(self->msg->reply_id) { + struct tgl_message *MR = tgl_message_get (TLS, self->msg->reply_id); + if(MR) { + ret = tgl_Msg_FromTglMsg(MR); + } else { + Py_RETURN_NONE; + } + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_getreply_id (tgl_Msg *self, void *closure) +{ + PyObject *ret; + + if(self->msg->reply_id) { + ret = PyLong_FromLong(self->msg->reply_id); + } else { + Py_RETURN_NONE; + } + + Py_XINCREF(ret); + return ret; +} + +static PyObject * +tgl_Msg_repr(tgl_Msg *self) +{ + PyObject *ret; + + ret = PyUnicode_FromFormat("", + self->msg->id, self->msg->flags, + PyObject_GetAttrString((PyObject*)self, "mention"), + PyObject_GetAttrString((PyObject*)self, "out"), + PyObject_GetAttrString((PyObject*)self, "unread"), + PyObject_GetAttrString((PyObject*)self, "service"), + PyObject_GetAttrString((PyObject*)self, "src"), + PyObject_GetAttrString((PyObject*)self, "dest"), + PyObject_GetAttrString((PyObject*)self, "text"), + PyObject_GetAttrString((PyObject*)self, "media"), + PyObject_GetAttrString((PyObject*)self, "date"), + PyObject_GetAttrString((PyObject*)self, "fwd_src"), + PyObject_GetAttrString((PyObject*)self, "fwd_date"), + PyObject_GetAttrString((PyObject*)self, "reply_id"), + PyObject_GetAttrString((PyObject*)self, "reply") + ); + + return ret; +} + + +static PyGetSetDef tgl_Msg_getseters[] = { + {"id", (getter)tgl_Msg_getid, NULL, "", NULL}, + {"flags", (getter)tgl_Msg_getflags, NULL, "", NULL}, + {"mention", (getter)tgl_Msg_getmention, NULL, "", NULL}, + {"out", (getter)tgl_Msg_getout, NULL, "", NULL}, + {"unread", (getter)tgl_Msg_getunread, NULL, "", NULL}, + {"service", (getter)tgl_Msg_getservice, NULL, "", NULL}, + {"src", (getter)tgl_Msg_getsrc, NULL, "", NULL}, + {"dest", (getter)tgl_Msg_getdest, NULL, "", NULL}, + {"text", (getter)tgl_Msg_gettext, NULL, "", NULL}, + {"media", (getter)tgl_Msg_getmedia, NULL, "", NULL}, + {"date", (getter)tgl_Msg_getdate, NULL, "", NULL}, + {"fwd_src", (getter)tgl_Msg_getfwd_src, NULL, "", NULL}, + {"fwd_date", (getter)tgl_Msg_getfwd_date, NULL, "", NULL}, + {"reply", (getter)tgl_Msg_getreply, NULL, "", NULL}, + {"reply_id", (getter)tgl_Msg_getreply_id, NULL, "", NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef tgl_Msg_members[] = { + {NULL} /* Sentinel */ +}; + + + +static PyMethodDef tgl_Msg_methods[] = { + {NULL} /* Sentinel */ +}; + + +PyTypeObject tgl_MsgType = { + PyVarObject_HEAD_INIT(NULL, 0) + "tgl.Msg", /* tp_name */ + sizeof(tgl_Msg), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)tgl_Msg_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)tgl_Msg_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "tgl Message", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + tgl_Msg_methods, /* tp_methods */ + tgl_Msg_members, /* tp_members */ + tgl_Msg_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)tgl_Msg_init, /* tp_init */ + 0, /* tp_alloc */ + tgl_Msg_new, /* tp_new */ +}; + + +PyObject * +tgl_Msg_FromTglMsg(struct tgl_message *msg) { + tgl_Msg *self = (tgl_Msg *) tgl_Msg_new((PyTypeObject *)&tgl_MsgType, Py_None, Py_None); + + self->msg = msg; + return (PyObject *) self; +} + +#endif diff --git a/python-types.h b/python-types.h new file mode 100644 index 0000000..448516a --- /dev/null +++ b/python-types.h @@ -0,0 +1,21 @@ +#ifndef __PYTHON_TYPES_H__ +#define __PYTHON_TYPES_H__ + +#include +#include + +typedef struct { + PyObject_HEAD + tgl_peer_t *peer; +} tgl_Peer; + +typedef struct { + PyObject_HEAD + struct tgl_message *msg; +} tgl_Msg; + + +PyObject * tgl_Peer_FromTglPeer(tgl_peer_t *peer); +PyObject * tgl_Msg_FromTglMsg(struct tgl_message *peer); + +#endif diff --git a/test.lua b/test.lua index 00d2a6b..321f345 100644 --- a/test.lua +++ b/test.lua @@ -57,17 +57,6 @@ function get_title (P, Q) end end -local lgi = require ('lgi') -local notify = lgi.require('Notify') -notify.init ("Telegram updates") -local icon = os.getenv("HOME") .. "/.telegram-cli/telegram-pics/telegram_64.png" - -function do_notify (user, msg) - local n = notify.Notification.new(user, msg, icon) - n:show () -end - --- }}} function on_msg_receive (msg) if started == 0 then @@ -76,7 +65,6 @@ function on_msg_receive (msg) if msg.out then return end - do_notify (get_title (msg.from, msg.to), msg.text) if (msg.text == 'ping') then if (msg.to.id == our_id) then diff --git a/tg-test.py b/tg-test.py new file mode 100644 index 0000000..fddd387 --- /dev/null +++ b/tg-test.py @@ -0,0 +1,71 @@ +import tgl +import pprint +from functools import partial + + +our_id = 0 +pp = pprint.PrettyPrinter(indent=4) + +binlog_done = False; + +def on_binlog_replay_end(): + binlog_done = True; + +def on_get_difference_end(): + pass + +def on_our_id(id): + our_id = id + return "Set ID: " + str(our_id) + +def msg_cb(success, msg): + pp.pprint(success) + pp.pprint(msg) + +HISTORY_QUERY_SIZE = 100 + +def history_cb(msg_list, peer, success, msgs): + print(len(msgs)) + msg_list.extend(msgs) + print(len(msg_list)) + if len(msgs) == HISTORY_QUERY_SIZE: + tgl.get_history(peer, len(msg_list), HISTORY_QUERY_SIZE, partial(history_cb, msg_list, peer)); + + +def cb(success): + print(success) + +def on_msg_receive(msg): + if msg.out and not binlog_done: + return; + + if msg.dest.id == our_id: # direct message + peer = msg.src + else: # chatroom + peer = msg.dest + + pp.pprint(msg) + if msg.text.startswith("!ping"): + print("SENDING PONG") + peer.send_msg("PONG!", msg_cb) + peer.send_contact(msg.src.phone, msg.src.first_name, msg.src.last_name , cb) + + +def on_secret_chat_update(peer, types): + return "on_secret_chat_update" + +def on_user_update(): + pass + +def on_chat_update(): + pass + +# Set callbacks +tgl.set_on_binlog_replay_end(on_binlog_replay_end) +tgl.set_on_get_difference_end(on_get_difference_end) +tgl.set_on_our_id(on_our_id) +tgl.set_on_msg_receive(on_msg_receive) +tgl.set_on_secret_chat_update(on_secret_chat_update) +tgl.set_on_user_update(on_user_update) +tgl.set_on_chat_update(on_chat_update) +