Skip to content

Commit

Permalink
Add Python intpreter to Eggdrop
Browse files Browse the repository at this point in the history
Patch by: Geo, thommey

PYTHON!

- Adds the .python partyline command to run a python command, similar to .tcl
- Adds the pysource Tcl command to load a python file, similar to the source Tcl command
- Imports the existing Tcl API (run via the eggdrop.tcl module)
  • Loading branch information
vanosg authored Jan 14, 2024
1 parent bf2c80b commit 4146828
Show file tree
Hide file tree
Showing 27 changed files with 4,506 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ config.cache
eggdrop
EGGMOD.stamp
mod.xlibs
__pycache__
3 changes: 2 additions & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ install-doc: install-start
cd doc/ && $(MAKE_INSTALL) install

install-scripts: install-start
+@cd scripts/ && $(MAKE_INSTALL) install
+@cd scripts/ && $(MAKE_INSTALL) install; \
cd ../src/mod && $(MAKE_INSTALL) install-scripts

#safety hash
3 changes: 3 additions & 0 deletions aclocal.m4
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ dnl
dnl Load tcl macros
builtin(include,m4/tcl.m4)

dnl Load python macros
builtin(include,m4/python.m4)

dnl Load gnu autoconf archive macros
builtin(include,m4/ax_create_stdint_h.m4)
builtin(include,m4/ax_lib_socket_nsl.m4)
Expand Down
48 changes: 48 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@ EGG_CROSS_COMPILING
MOD_UPDIR
DEST
EGGVERSION
egg_with_python_config
egg_enable_python
SSL_LIBS
SSL_INCLUDES
DEBCFLGS
Expand Down Expand Up @@ -801,6 +803,8 @@ enable_tls
with_sslinc
with_ssllib
enable_tdns
enable_python
with_python_config
'
ac_precious_vars='build_alias
host_alias
Expand Down Expand Up @@ -1458,6 +1462,9 @@ Optional Features and Packages:
--with-sslinc=PATH Path to OpenSSL headers
--with-ssllib=PATH Path to OpenSSL libraries
--disable-tdns disable threaded DNS core
--enable-python enable Python support (autodetect)
--disable-python disable Python support
--with-python-config=PATH Path to python-config
Some influential environment variables:
CC C compiler command
Expand Down Expand Up @@ -10603,6 +10610,47 @@ printf "%s\n" "#define EGG_TDNS 1" >>confdefs.h
fi
# Check for Python
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to compile the Python module" >&5
$as_echo_n "checking whether to compile the Python module... " >&6; }
# Check whether --enable-python was given.
if test "${enable_python+set}" = set; then :
enableval=$enable_python; egg_enable_python="$enableval"
fi
# Check whether --enable-python was given.
if test "${enable_python+set}" = set; then :
enableval=$enable_python; egg_enable_python="$enableval"
else
egg_enable_python="autodetect"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $egg_enable_python" >&5
$as_echo "$egg_enable_python" >&6; }
# Check whether --with-python-config was given.
if test "${with_python_config+set}" = set; then :
withval=$with_python_config;
if test "x$enable_python" != "xno"; then
if test -d "$withval" || test -x "$withval"; then
egg_with_python_config="$withval"
else
egg_with_python_config="no"
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Invalid path to python-config. $withval is not a directory and not an executable." >&5
$as_echo "$as_me: WARNING: Invalid path to python-config. $withval is not a directory and not an executable." >&2;}
fi
fi
fi
# Substitute Makefile variables.
Expand Down
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ EGG_TLS_DETECT
# Threaded DNS core
EGG_TDNS_ENABLE

# Check for Python
EGG_PYTHON_ENABLE
EGG_PYTHON_WITHCONFIG


# Substitute Makefile variables.
EGG_SUBST_EGGVERSION
Expand Down
149 changes: 149 additions & 0 deletions doc/sphinx_source/modules/mod/python.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
Last revised: November 03, 2023

.. _python:

============
Python Module
============

This module adds a Python interpreter to Eggdrop, allowing you to run Python scripts.

--------------
Loading Python
--------------

Put this line into your Eggdrop configuration file to load the python module::

loadmodule python

To load a python script from your config file, place the .py file in the scripts/ folder and add the following line to your config::

pysource scripts/myscript.py

------------------
Partyline Commands
------------------

^^^^^^^^^^^^^^^^^^^
python <expression>
^^^^^^^^^^^^^^^^^^^

You can run a python command from the partyline with the .python command, such as::

.python 1 + 1
Python: 2
.python from eggdrop.tcl import putmsg; putmsg('#chan', 'Hello world!')
Python: None

^^^^^^^^^^^^^
.binds python
^^^^^^^^^^^^^

The python module extends the core ``.binds`` partyline command by adding a ``python`` mask. This command will list all binds for python scripts.

------------
Tcl Commands
------------

^^^^^^^^^^^^^^^^^^^^^^^
pysource <path/to/file>
^^^^^^^^^^^^^^^^^^^^^^^

The ``pysource`` command is analgous to the Tcl ``source`` command, except that it loads a Python script into Eggdrop instead of a Tcl script.

-----------------------
Eggdrop Python Commands
-----------------------

The Python module is built to use the existing core Tcl commands integrated into Eggdrop via the ``eggdrop.tcl`` module. To call an existing Tcl command from Python, you can either load the entire catalog by running ``import eggdrop.tcl``, or be more specific by ``from eggdrop.tcl import putserv, putlog, chanlist``, etc.

Arguments to the Tcl functions are automatically converted as follows:

* ``None`` is converted to an empty Tcl object (the empty string, ``""``)
* ``List`` and ``Tuple`` is converted to a ``Tcl list``
* ``Dict`` is converted to a ``Tcl dictionary``
* Everything else is converted to a string using the str() method

Return values from Tcl functions must be manually converted:

* ``""`` the empty string is automatically converted to None
* everything else is returned as string
* ``Tcl list`` as string can be converted to a Python ``List`` using ``parse_tcl_list``
* ``Tcl dictionary`` as string can be converted to a Python ``Dict`` using ``parse_tcl_list``

Additionally, a few extra python commands have been created for use without these conversions:

^^^^^^^^^^^^^^^^
bind <arguments>
^^^^^^^^^^^^^^^^

The python version of the bind command is used to create a bind that triggers a python function. The python bind takes the same arguments as the Tcl binds, for more information on bind argument syntax please see tcl_binds_. The eggdrop.tcl.bind command should not be used as it will attempt to call a Tcl proc.

^^^^^^^^^^^^^^^^^^^^^^^
parse_tcl_list <string>
^^^^^^^^^^^^^^^^^^^^^^^

When a python script calls a Tcl command that returns a list via the eggdrop.tcl module, the return value will be a Tcl-formatted list- also simply known as a string. The ``parse_tcl_list`` command will convert the Tcl-formatted list into a Python list, which can then freely be used within the Python script.

^^^^^^^^^^^^^^^^^^^^^^^
parse_tcl_dict <string>
^^^^^^^^^^^^^^^^^^^^^^^

When a python script calls a Tcl command that returns a dict via the eggdrop.tcl module, the return value will be a Tcl-formatted dict- also simply known as a string. The ``parse_tcl_dict`` command will c
onvert the Tcl-formatted dict into a Python list, which can then freely be used within the Python script.

----------------
Config variables
----------------

There are also some variables you can set in your config file:

set allow-resync 0
When two bots get disconnected, this setting allows them to create a
resync buffer which saves all changes done to the userfile during
the disconnect. When they reconnect, they will not have to transfer
the complete user file, but, instead, just send the resync buffer.

--------------------------------
Writing an Eggdrop Python script
--------------------------------

This is how to write a python script for Eggdrop.

You can view examples of Python scripts in the exampleScripts folder included with this module.

.. glossary::
bestfriend.py
This example script demonstrates how to use the parse_tcl_list() python command to convert a list returned by a Tcl command into a list that is usable by Python.
greet.py
This is a very basic script that demonstrates how a Python script with binds can be run by Eggdrop.
imdb.py
This script shows how to use an existing third-party module to extend a Python script, in this case retrieving information from imdb.com.
listtls.py
This script demonstrates how to use parse-tcl_list() and parse_tcl_dict() to convert a list of dicts provided by Tcl into something that is usuable by Python.
urltitle.py
This script shows how to use an existing third-party module to extend a Python script, in this case using an http parser to collect title information from a provided web page.


^^^^^^^^^^^^^^
Header section
^^^^^^^^^^^^^^

An Eggdrop python script requires you to import X Y and Z, in this format.

^^^^^^^^^^^^
Code Section
^^^^^^^^^^^^

Normal python code works here. To run a command from the Eggdrop Tcl library, use this format.

Use this format all over.

-------------------------------------
Writing a module for use with Eggdrop
-------------------------------------

This is how you import a module for use with an egg python script.


Copyright (C) 2000 - 2023 Eggheads Development Team
3 changes: 3 additions & 0 deletions doc/sphinx_source/using/tcl-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2227,6 +2227,8 @@ unbind <type> <flags> <keyword/mask> <proc-name>
binds [type/mask]
^^^^^^^^^^^^^^^^^

Description: By default, lists Tcl binds registered with the Eggdrop. You can specify 'all' to view all binds, 'tcl' to view Tcl binds, and 'python' to view Python binds. Alternately, you can specify a bind type (pub, msg, etc) to view all binds that match that type of bind, or a mask that is matched against the command associated with the bind.

Returns: a list of Tcl binds, each item in the list is a sublist of five elements:
{<type> <flags> <name> <hits> <proc>}

Expand Down Expand Up @@ -2897,6 +2899,7 @@ Removing a bind
To remove a bind, use the 'unbind' command. For example, to remove the
bind for the "stop" msg command, use 'unbind msg - stop msg:stop'.

.. _tcl_binds:

^^^^^^^^^^
Flag Masks
Expand Down
53 changes: 53 additions & 0 deletions m4/python.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
dnl python.m4 -- Autoconf macros to compile python.mod
dnl
dnl Copyright (c) 2022 - 2022 Eggheads Development Team
dnl
dnl This program is free software; you can redistribute it and/or
dnl modify it under the terms of the GNU General Public License
dnl as published by the Free Software Foundation; either version 2
dnl of the License, or (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
dnl

dnl EGG_PYTHON_ENABLE
dnl
AC_DEFUN([EGG_PYTHON_ENABLE],
[
AC_MSG_CHECKING([whether to compile the Python module])
AC_ARG_ENABLE(python,
[ --enable-python enable Python support (autodetect)],
[egg_enable_python="$enableval"])
AC_ARG_ENABLE(python,
[ --disable-python disable Python support], [egg_enable_python="$enableval"],
[egg_enable_python="autodetect"])
AC_MSG_RESULT([$egg_enable_python])
AC_SUBST(egg_enable_python)
])


dnl EGG_PYTHON_WITHCONFIG
dnl
AC_DEFUN(EGG_PYTHON_WITHCONFIG,
[
AC_ARG_WITH(python-config, [ --with-python-config=PATH Path to python-config], [
if test "x$enable_python" != "xno"; then
if test -d "$withval" || test -x "$withval"; then
egg_with_python_config="$withval"
else
egg_with_python_config="no"
AC_MSG_WARN([Invalid path to python-config. $withval is not a directory and not an executable.])
fi
fi
])
AC_SUBST(egg_with_python_config)
])

2 changes: 2 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ extern sigjmp_buf alarmret;
time_t now;
static int argc;
static char **argv;
char *argv0;

/*
* Please use the PATCH macro instead of directly altering the version
Expand Down Expand Up @@ -981,6 +982,7 @@ int main(int arg_c, char **arg_v)

argc = arg_c;
argv = arg_v;
argv0 = argv[0];

/* Version info! */
#ifdef EGG_PATCH
Expand Down
14 changes: 13 additions & 1 deletion src/mod/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ distclean:
fi; \
done

install: install-help install-language
install: install-help install-language install-scripts

install-help:
@echo "Copying module help files." && \
Expand Down Expand Up @@ -169,4 +169,16 @@ install-language:
fi; \
done;

install-scripts:
@for i in $(mods); do \
if test ! "x`echo $(srcdir)/$$i/scripts/*`" = "x$(srcdir)/$$i/scripts/*"; then \
echo "Installing example $$i scripts"; \
for s in $(srcdir)/$$i/scripts/*; do \
if test ! -f "$(DEST)/scripts/`basename "$$s"`"; then \
$(INSTALL_DATA) $$s $(DEST)/scripts/; \
fi; \
done; \
fi; \
done;

#safety hash
6 changes: 4 additions & 2 deletions src/mod/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,10 @@ typedef void (*chanout_butfunc)(int, int, const char *, ...) ATTRIBUTE_FORMAT(pr
#define get_user_by_account ((struct userrec * (*)(char *))global[317])
#define delaccount_by_handle ((int(*)(char *,char *))global[318])
#define check_tcl_event_arg ((void (*) (const char *,const char *))global[319])
/*320 - 323 */

/* 320 - 323 */
#define bind_bind_entry ((int(*)(tcl_bind_list_t *, const char *, const char *, const char *))global[320])
#define unbind_bind_entry ((int(*)(tcl_bind_list_t *, const char *, const char *, const char *))global[321])
#define argv0 ((char *)global[322])



Expand Down
Loading

0 comments on commit 4146828

Please sign in to comment.