diff -Nru limnoria-2023.1.28/debian/changelog limnoria-2023.5.27/debian/changelog --- limnoria-2023.1.28/debian/changelog 2023-03-18 03:46:57.000000000 +0000 +++ limnoria-2023.5.27/debian/changelog 2023-06-15 03:11:29.000000000 +0000 @@ -1,8 +1,26 @@ -limnoria (2023.1.28-1~bpo22.04.1) jammy-backports; urgency=medium +limnoria (2023.5.27-1~bpo22.04.1) jammy-backports; urgency=medium * No-change backport to jammy. - -- Unit 193 Fri, 17 Mar 2023 23:46:57 -0400 + -- Unit 193 Wed, 14 Jun 2023 23:11:29 -0400 + +limnoria (2023.5.27-1) unstable; urgency=medium + + * New upstream version 2023.5.27. + + -- Unit 193 Sat, 27 May 2023 18:28:16 -0400 + +limnoria (2023.4.28-1) unstable; urgency=medium + + [ Mattia Rizzolo ] + * d/rules: Drop the nocheck check that is now taken care of by debhelper v13. + + [ Unit 193 ] + * New upstream version 2023.4.28. + * d/control, d/rules, d/t/upstream-tests: + - B-D on pybuild-plugin-pyproject, update tests accordingly. + + -- Unit 193 Wed, 24 May 2023 19:30:55 -0400 limnoria (2023.1.28-1) unstable; urgency=medium diff -Nru limnoria-2023.1.28/debian/control limnoria-2023.5.27/debian/control --- limnoria-2023.1.28/debian/control 2023-01-13 08:09:44.000000000 +0000 +++ limnoria-2023.5.27/debian/control 2023-05-24 23:28:44.000000000 +0000 @@ -9,6 +9,7 @@ dh-sequence-python3, iputils-ping , procps, + pybuild-plugin-pyproject, python3-all, python3-cryptography , python3-dateutil , diff -Nru limnoria-2023.1.28/debian/copyright limnoria-2023.5.27/debian/copyright --- limnoria-2023.1.28/debian/copyright 2023-01-13 08:09:19.000000000 +0000 +++ limnoria-2023.5.27/debian/copyright 2023-05-24 22:32:41.000000000 +0000 @@ -12,7 +12,7 @@ License: BSD-3-clause Files: debian/* -Copyright: 2014-2021 Mattia Rizzolo +Copyright: 2014-2023 Mattia Rizzolo 2018-2023 Unit 193 License: BSD-3-clause diff -Nru limnoria-2023.1.28/debian/rules limnoria-2023.5.27/debian/rules --- limnoria-2023.1.28/debian/rules 2021-08-03 04:10:29.000000000 +0000 +++ limnoria-2023.5.27/debian/rules 2023-05-24 23:28:59.000000000 +0000 @@ -7,17 +7,13 @@ dh $@ --buildsystem pybuild override_dh_auto_test: -ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) LC_ALL=C.UTF-8 \ PYBUILD_SYSTEM=custom \ PYBUILD_TEST_ARGS="PYTHONPATH={build_dir} \ - build/scripts-{version}/supybot-test \ + python{version} {build_dir}/supybot/scripts/limnoria_test.py \ test \ -v \ --no-setuid \ --no-network \ --plugins-dir={build_dir}/supybot/plugins/" \ dh_auto_test -else - @echo "Skipping test suite as DEB_BUILD_OPTIONS=nocheck is set" -endif diff -Nru limnoria-2023.1.28/debian/tests/upstream-tests limnoria-2023.5.27/debian/tests/upstream-tests --- limnoria-2023.1.28/debian/tests/upstream-tests 2021-08-03 04:10:29.000000000 +0000 +++ limnoria-2023.5.27/debian/tests/upstream-tests 2023-05-24 23:29:22.000000000 +0000 @@ -7,13 +7,13 @@ export LC_ALL=C.UTF-8 cp -rv test "$AUTOPKGTEST_TMP" -cp -v scripts/supybot-test "$AUTOPKGTEST_TMP" +cp -v src/scripts/limnoria_test.py "$AUTOPKGTEST_TMP" cd "$AUTOPKGTEST_TMP" for python in $(py3versions --supported);do printf "###\\n### Now testing %s\\n###\\n" "$python" - "$python" ./supybot-test test \ + "$python" ./limnoria_test.py test \ -v \ --no-network \ --plugins-dir=/usr/lib/python3/dist-packages/supybot/plugins/ diff -Nru limnoria-2023.1.28/limnoria.egg-info/entry_points.txt limnoria-2023.5.27/limnoria.egg-info/entry_points.txt --- limnoria-2023.1.28/limnoria.egg-info/entry_points.txt 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/limnoria.egg-info/entry_points.txt 2023-05-27 06:02:35.000000000 +0000 @@ -0,0 +1,17 @@ +[console_scripts] +limnoria = supybot.scripts.limnoria:main +limnoria-adduser = supybot.scripts.limnoria_adduser:main +limnoria-botchk = supybot.scripts.limnoria_botchk:main +limnoria-plugin-create = supybot.scripts.limnoria_plugin_create:main +limnoria-plugin-doc = supybot.scripts.limnoria_plugin_doc:main +limnoria-reset-password = supybot.scripts.limnoria_reset_password:main +limnoria-test = supybot.scripts.limnoria_test:main +limnoria-wizard = supybot.scripts.limnoria_wizard:main +supybot = supybot.scripts.limnoria:main +supybot-adduser = supybot.scripts.limnoria_adduser:main +supybot-botchk = supybot.scripts.limnoria_botchk:main +supybot-plugin-create = supybot.scripts.limnoria_plugin_create:main +supybot-plugin-doc = supybot.scripts.limnoria_plugin_doc:main +supybot-reset-password = supybot.scripts.limnoria_reset_password:main +supybot-test = supybot.scripts.limnoria_test:main +supybot-wizard = supybot.scripts.limnoria_wizard:main diff -Nru limnoria-2023.1.28/limnoria.egg-info/PKG-INFO limnoria-2023.5.27/limnoria.egg-info/PKG-INFO --- limnoria-2023.1.28/limnoria.egg-info/PKG-INFO 2023-02-11 09:15:17.000000000 +0000 +++ limnoria-2023.5.27/limnoria.egg-info/PKG-INFO 2023-05-27 06:02:35.000000000 +0000 @@ -1,12 +1,11 @@ Metadata-Version: 2.1 Name: limnoria -Version: 2023.1.28 +Version: 2023.5.27 Summary: A multipurpose Python IRC bot, designed for flexibility and robustness , while being easy to install, set up, and maintain. Home-page: https://limnoria.net/ +Download-URL: https://pypi.python.org/pypi/limnoria Author: Valentin Lorentz Author-email: progval+limnoria@progval.net -License: UNKNOWN -Download-URL: https://pypi.python.org/pypi/limnoria Platform: linux Platform: linux2 Platform: win32 @@ -31,10 +30,11 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: Communications :: Chat :: Internet Relay Chat Classifier: Topic :: Software Development :: Libraries :: Python Modules Provides: supybot License-File: LICENSE.md A robust, full-featured Python IRC bot with a clean and flexible plugin API. Equipped with a complete ACL system for specifying user permissions with as much as per-command granularity. Batteries are included in the form of numerous plugins already written. - diff -Nru limnoria-2023.1.28/limnoria.egg-info/SOURCES.txt limnoria-2023.5.27/limnoria.egg-info/SOURCES.txt --- limnoria-2023.1.28/limnoria.egg-info/SOURCES.txt 2023-02-11 09:15:17.000000000 +0000 +++ limnoria-2023.5.27/limnoria.egg-info/SOURCES.txt 2023-05-27 06:02:35.000000000 +0000 @@ -8,6 +8,7 @@ limnoria.egg-info/PKG-INFO limnoria.egg-info/SOURCES.txt limnoria.egg-info/dependency_links.txt +limnoria.egg-info/entry_points.txt limnoria.egg-info/top_level.txt locales/__init__.py locales/de.po @@ -16,6 +17,14 @@ locales/fr.py locales/it.po locales/messages.pot +man/limnoria-adduser.1 +man/limnoria-botchk.1 +man/limnoria-plugin-create.1 +man/limnoria-plugin-doc.1 +man/limnoria-reset-password.1 +man/limnoria-test.1 +man/limnoria-wizard.1 +man/limnoria.1 man/supybot-adduser.1 man/supybot-botchk.1 man/supybot-plugin-create.1 @@ -536,14 +545,6 @@ plugins/Web/locales/fi.po plugins/Web/locales/fr.po plugins/Web/locales/it.po -scripts/supybot -scripts/supybot-adduser -scripts/supybot-botchk -scripts/supybot-plugin-create -scripts/supybot-plugin-doc -scripts/supybot-reset-password -scripts/supybot-test -scripts/supybot-wizard src/__init__.py src/ansi.py src/callbacks.py @@ -572,6 +573,15 @@ src/world.py src/drivers/Socket.py src/drivers/__init__.py +src/scripts/__init__.py +src/scripts/limnoria.py +src/scripts/limnoria_adduser.py +src/scripts/limnoria_botchk.py +src/scripts/limnoria_plugin_create.py +src/scripts/limnoria_plugin_doc.py +src/scripts/limnoria_reset_password.py +src/scripts/limnoria_test.py +src/scripts/limnoria_wizard.py src/utils/__init__.py src/utils/crypt.py src/utils/error.py diff -Nru limnoria-2023.1.28/man/limnoria.1 limnoria-2023.5.27/man/limnoria.1 --- limnoria-2023.1.28/man/limnoria.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,66 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria.1 +.\" +.TH LIMNORIA 1 "JULY 2009" +.SH NAME +limnoria - A robust and user friendly Python IRC bot +.SH SYNOPSIS +.B limnoria +.RI [ options ] " configFile +.SH DESCRIPTION +.B Limnoria +is a robust, user-friendly, and programmer-friendly Python IRC bot. +It aims to be an adequate replacement for most existing IRC bots. It +includes a very flexible and powerful ACL system for controlling access +to commands, as well as more than 50 builtin plugins providing around +400 actual commands. +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BR \-P ", " \-\^\-profile +Enable profiling. +.TP +.BI \-n " NICK" "\fR,\fP \-\^\-nick=" NICK +Nick the bot should use. +.TP +.BI \-u " USER" "\fR,\fP \-\^\-user=" USER +Full username the bot should use. +.TP +.BI \-i " IDENT" "\fR,\fP \-\^\-ident=" IDENT +Ident the bot should use. +.TP +.BR \-d ", " \-\^\-daemon +Determines whether the bot will daemonize. This is a no-op on +non-POSIX systems. +.TP +.B \-\^\-allow\-default\-owner +Determines whether the bot will allow its defaultCapabilities not to +include "\-owner", thus giving all users the owner capability by +default. This is dumb, hence we require a command-line option to +enable it. +.TP +.B \-\^\-allow\-root +Determines whether the bot will be allowed to run as root. You do not +want this. Do not do it. Even if you think you want it, you do not. +.TP +.B \-\^\-debug +Determines whether some extra debugging stuff will be logged by this +script. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/limnoria-adduser.1 limnoria-2023.5.27/man/limnoria-adduser.1 --- limnoria-2023.1.28/man/limnoria-adduser.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-adduser.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,42 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-adduser.1 +.\" +.TH LIMNORIA-ADDUSER 1 "APRIL 2005" +.SH NAME +limnoria-adduser \- Adds a user to a Limnoria users.conf file +.SH SYNOPSIS +.B limnoria-adduser +.RI [ options ] " users.conf +.SH DESCRIPTION +.B limnoria-adduser +adds a user to the specified users.conf file. +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BR \-u " NAME" "\fR,\fP \-\^\-username=" NAME +Specifies the username to use for the new user. +.TP +.BR \-p " PASSWORD" "\fR,\fP \-\^\-password=" PASSWORD +Specifies the password to use for the new user. +.TP +.BR \-c " CAPABILITY" "\fR,\fP \-\^\-capability=" CAPABILITY +Capability the user should have; this option may be given +multiple times. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/limnoria-botchk.1 limnoria-2023.5.27/man/limnoria-botchk.1 --- limnoria-2023.1.28/man/limnoria-botchk.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-botchk.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,54 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-botchk.1 +.\" +.TH LIMNORIA-BOTCHK 1 "APRIL 2005" +.SH NAME +limnoria-botchk \- A script to start Limnoria if it's not already running. +.SH SYNOPSIS +.B limnoria-botchk +.RI [ options ] +.SH DESCRIPTION +.B limnoria-botchk +is a script that will start Limnoria if it detects that one is not currently +running. This can be useful for scheduling +.IR limnoria (1) +to run via +.IR cron (8). +.SH OPTIONS +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BR \-v ", " \-\^\-verbose +Use verbose output when running the script. +.TP +.BI \-\^\-botdir= BOTDIR +Determines which directory the bot be started in. +.TP +.BI \-\^\-pidfile= PIDFILE +Specifies the name of the pidfile to look for. This should be relative +to the given botdir. +.TP +.BI \-\^\-limnoria= LIMNORIA +Specifies the location of +.IR limnoria (1). +If this is not given, it is assumed that +.IR limnoria (1) +is in the user's $PATH. +.TP +.BI \-\^\-conffile= CONFFILE +Specifies the path to the bot's configuration file. This will be used +when (re)starting the bot. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/limnoria-plugin-create.1 limnoria-2023.5.27/man/limnoria-plugin-create.1 --- limnoria-2023.1.28/man/limnoria-plugin-create.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-plugin-create.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,43 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-plugin-create.1 +.\" +.TH LIMNORIA-PLUGIN-CREATE 1 "APRIL 2005" +.SH NAME +limnoria-plugin-create \- A wizard for creating Limnoria plugins +.SH SYNOPSIS +.B limnoria-plugin-create +.RI [ options ] +.SH DESCRIPTION +.B limnoria-plugin-create +is a wizard that creates a template python source file for a new +.IR limnoria (1) +plugin. +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BI \-n " NAME" "\fR,\fP \-\^\-name=" NAME +Sets the name for the plugin. +.TP +.BR \-t ", " \-\^\-thread +Makes the plugin threaded. +.TP +.BI \-\^\-real\-name= REALNAME +Specify what real name the copyright is assigned to. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/limnoria-plugin-doc.1 limnoria-2023.5.27/man/limnoria-plugin-doc.1 --- limnoria-2023.1.28/man/limnoria-plugin-doc.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-plugin-doc.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,48 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-plugin-doc.1 +.\" +.TH LIMNORIA-PLUGIN-DOC 1 "May 2009" +.SH NAME +limnoria-plugin-doc \- Generates the documentation for a Limnoria plugin +.SH SYNOPSIS +.B limnoria-plugin-doc +.RI [ options ] +.SH DESCRIPTION +.B limnoria-plugin-doc +is used to generate documentation (StructuredText or reStructuredText format) +for a +.IR limnoria (1) +plugin. +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BR \-c ", " \-\^\-clean +Clean the various data/conf/log directories after generating the docs. +.TP +.BR \-o ", " \-\^\-output\-dir= \fIOUTPUTDIR +Specifies the directory in which to write the documentation for the plugin. +.TP +.BR \-f ", " \-\^\-format= \fIFORMAT +Specifies which output format to use. Choices are 'rst' or 'stx'. +.TP +.BI \-\^\-plugins\-dir= PLUGINSDIRS +Looks in the given directory for plugins and generates documentation for all of +them. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-create (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/limnoria-reset-password.1 limnoria-2023.5.27/man/limnoria-reset-password.1 --- limnoria-2023.1.28/man/limnoria-reset-password.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-reset-password.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,35 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-reset-password.1 +.\" +.TH LIMNORIA-RESET-PASSWORD 1 "JUNE 2022" +.SH NAME +limnoria-reset-password \- Changes a user's password in a Limnoria users.conf file +.SH SYNOPSIS +.B limnoria-reset-password +.RI [ options ] " users.conf +.SH DESCRIPTION +.B limnoria-reset-password +changes a user's password in a Limnoria users.conf file +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BR \-u " NAME" "\fR,\fP \-\^\-username=" NAME +Specifies the username to use for the new user. +.TP +.BR \-p " PASSWORD" "\fR,\fP \-\^\-password=" PASSWORD +Specifies the new password to use for the new user. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-adduser (1) +.SH AUTHOR +This manual page was originally written by Valentin Lorentz +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. + diff -Nru limnoria-2023.1.28/man/limnoria-test.1 limnoria-2023.5.27/man/limnoria-test.1 --- limnoria-2023.1.28/man/limnoria-test.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-test.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,51 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-test.1 +.\" +.TH LIMNORIA-TEST 1 "OCTOBER 2005" +.SH NAME +limnoria-test \- Runs the test suite for a Limnoria plugin +.SH SYNOPSIS +.B limnoria-test +.RI [ options ] " plugins +.SH DESCRIPTION +.B limnoria-test +Runs the test suite for a Limnoria plugin +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.BR \-c ", " \-\^\-clean +Cleans the various data/conf/logs directories before running tests. +.TP +.BR \-t " TIMEOUT" "\fR,\fP \-\^\-timeout=" TIMEOUT +Specifies the timeout for tests to return responses. +.TP +.BR \-v ", " \-\^\-verbose +Sets the verbose flag, logging extra information about each test that runs. +.TP +.BR \-\^\-no\-network +Prevents the network-based tests from being run. +.TP +.BR \-\^\-trace +Traces all calls made. Unless you're really in a pinch, you probably +shouldn't do this; it results in copious amounts of output. +.TP +.BR "\fR,\fP \-\^\-plugins\-dir=" PLUGINSDIR +Looks in the given directory for plugins and loads the tests for all of them. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/limnoria-wizard.1 limnoria-2023.5.27/man/limnoria-wizard.1 --- limnoria-2023.1.28/man/limnoria-wizard.1 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/man/limnoria-wizard.1 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,42 @@ +.\" Process this file with +.\" groff -man -Tascii limnoria-wizard.1 +.\" +.TH LIMNORIA-WIZARD 1 "SEPTEMBER 2004" +.SH NAME +limnoria-wizard \- A wizard for creating Limnoria configuration files +.SH SYNOPSIS +.B limnoria-wizard +.RI [ options ] +.SH DESCRIPTION +.B limnoria-wizard +is an in-depth wizard that provides a nice user interface for creating +configuration files for +.IR limnoria (1). +.SH OPTIONS +.TP +.B \-\^\-version +Show version of program. +.TP +.BR \-h ", " \-\^\-help +Show summary of options. +.TP +.B \-\^\-allow\-root +Determines whether the wizard will be allowed to run as root. You do not +want this. Do not do it. Even if you think you want it, you do not. +.TP +.B \-\^\-no\-network +Determines whether the wizard will be allowed to run without a network +connection. +.SH "SEE ALSO" +.IR python (1), +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) +.SH AUTHOR +This manual page was originally written by James McCoy +. Permission is granted to copy, +distribute and/or modify this document under the terms of the Limnoria +license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot.1 limnoria-2023.5.27/man/supybot.1 --- limnoria-2023.1.28/man/supybot.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,14 +1,14 @@ .\" Process this file with -.\" groff -man -Tascii supybot.1 +.\" groff -man -Tascii limnoria.1 .\" -.TH SUPYBOT 1 "JULY 2009" +.TH LIMNORIA 1 "JULY 2009" .SH NAME -supybot - A robust and user friendly Python IRC bot +limnoria - A robust and user friendly Python IRC bot .SH SYNOPSIS -.B supybot +.B limnoria .RI [ options ] " configFile .SH DESCRIPTION -.B Supybot +.B Limnoria is a robust, user-friendly, and programmer-friendly Python IRC bot. It aims to be an adequate replacement for most existing IRC bots. It includes a very flexible and powerful ACL system for controlling access @@ -53,14 +53,14 @@ script. .SH "SEE ALSO" .IR python (1), -.IR supybot-test (1), -.IR supybot-botchk (1), -.IR supybot-wizard (1), -.IR supybot-adduser (1), -.IR supybot-plugin-doc (1), -.IR supybot-plugin-create (1) +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-adduser.1 limnoria-2023.5.27/man/supybot-adduser.1 --- limnoria-2023.1.28/man/supybot-adduser.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-adduser.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,14 +1,14 @@ .\" Process this file with -.\" groff -man -Tascii supybot-adduser.1 +.\" groff -man -Tascii limnoria-adduser.1 .\" -.TH SUPYBOT-ADDUSER 1 "APRIL 2005" +.TH LIMNORIA-ADDUSER 1 "APRIL 2005" .SH NAME -supybot-adduser \- Adds a user to a Supybot users.conf file +limnoria-adduser \- Adds a user to a Limnoria users.conf file .SH SYNOPSIS -.B supybot-adduser +.B limnoria-adduser .RI [ options ] " users.conf .SH DESCRIPTION -.B supybot-adduser +.B limnoria-adduser adds a user to the specified users.conf file. .SH OPTIONS .TP @@ -29,14 +29,14 @@ multiple times. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-test (1), -.IR supybot-botchk (1), -.IR supybot-wizard (1), -.IR supybot-plugin-doc (1), -.IR supybot-plugin-create (1) +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-botchk.1 limnoria-2023.5.27/man/supybot-botchk.1 --- limnoria-2023.1.28/man/supybot-botchk.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-botchk.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,17 +1,17 @@ .\" Process this file with -.\" groff -man -Tascii supybot-botchk.1 +.\" groff -man -Tascii limnoria-botchk.1 .\" -.TH SUPYBOT-BOTCHK 1 "APRIL 2005" +.TH LIMNORIA-BOTCHK 1 "APRIL 2005" .SH NAME -supybot-botchk \- A script to start Supybot if it's not already running. +limnoria-botchk \- A script to start Limnoria if it's not already running. .SH SYNOPSIS -.B supybot-botchk +.B limnoria-botchk .RI [ options ] .SH DESCRIPTION -.B supybot-botchk -is a script that will start Supybot if it detects that one is not currently +.B limnoria-botchk +is a script that will start Limnoria if it detects that one is not currently running. This can be useful for scheduling -.IR supybot (1) +.IR limnoria (1) to run via .IR cron (8). .SH OPTIONS @@ -29,11 +29,11 @@ Specifies the name of the pidfile to look for. This should be relative to the given botdir. .TP -.BI \-\^\-supybot= SUPYBOT +.BI \-\^\-limnoria= LIMNORIA Specifies the location of -.IR supybot (1). +.IR limnoria (1). If this is not given, it is assumed that -.IR supybot (1) +.IR limnoria (1) is in the user's $PATH. .TP .BI \-\^\-conffile= CONFFILE @@ -41,14 +41,14 @@ when (re)starting the bot. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-test (1), -.IR supybot-wizard (1), -.IR supybot-adduser (1), -.IR supybot-plugin-doc (1), -.IR supybot-plugin-create (1) +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-plugin-create.1 limnoria-2023.5.27/man/supybot-plugin-create.1 --- limnoria-2023.1.28/man/supybot-plugin-create.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-plugin-create.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,16 +1,16 @@ .\" Process this file with -.\" groff -man -Tascii supybot-plugin-create.1 +.\" groff -man -Tascii limnoria-plugin-create.1 .\" -.TH SUPYBOT-PLUGIN-CREATE 1 "APRIL 2005" +.TH LIMNORIA-PLUGIN-CREATE 1 "APRIL 2005" .SH NAME -supybot-plugin-create \- A wizard for creating Supybot plugins +limnoria-plugin-create \- A wizard for creating Limnoria plugins .SH SYNOPSIS -.B supybot-plugin-create +.B limnoria-plugin-create .RI [ options ] .SH DESCRIPTION -.B supybot-plugin-create +.B limnoria-plugin-create is a wizard that creates a template python source file for a new -.IR supybot (1) +.IR limnoria (1) plugin. .SH OPTIONS .TP @@ -30,14 +30,14 @@ Specify what real name the copyright is assigned to. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-test (1), -.IR supybot-botchk (1), -.IR supybot-wizard (1), -.IR supybot-adduser (1), -.IR supybot-plugin-doc (1) +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-plugin-doc.1 limnoria-2023.5.27/man/supybot-plugin-doc.1 --- limnoria-2023.1.28/man/supybot-plugin-doc.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-plugin-doc.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,17 +1,17 @@ .\" Process this file with -.\" groff -man -Tascii supybot-plugin-doc.1 +.\" groff -man -Tascii limnoria-plugin-doc.1 .\" -.TH SUPYBOT-PLUGIN-DOC 1 "May 2009" +.TH LIMNORIA-PLUGIN-DOC 1 "May 2009" .SH NAME -supybot-plugin-doc \- Generates the documentation for a Supybot plugin +limnoria-plugin-doc \- Generates the documentation for a Limnoria plugin .SH SYNOPSIS -.B supybot-plugin-doc +.B limnoria-plugin-doc .RI [ options ] .SH DESCRIPTION -.B supybot-plugin-doc +.B limnoria-plugin-doc is used to generate documentation (StructuredText or reStructuredText format) for a -.IR supybot (1) +.IR limnoria (1) plugin. .SH OPTIONS .TP @@ -35,14 +35,14 @@ them. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-test (1), -.IR supybot-botchk (1), -.IR supybot-wizard (1), -.IR supybot-adduser (1), -.IR supybot-plugin-create (1) +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-create (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-reset-password.1 limnoria-2023.5.27/man/supybot-reset-password.1 --- limnoria-2023.1.28/man/supybot-reset-password.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-reset-password.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,15 +1,15 @@ .\" Process this file with -.\" groff -man -Tascii supybot-reset-password.1 +.\" groff -man -Tascii limnoria-reset-password.1 .\" -.TH SUPYBOT-RESET-PASSWORD 1 "JUNE 2022" +.TH LIMNORIA-RESET-PASSWORD 1 "JUNE 2022" .SH NAME -supybot-reset-password \- Changes a user's password in a Supybot users.conf file +limnoria-reset-password \- Changes a user's password in a Limnoria users.conf file .SH SYNOPSIS -.B supybot-reset-password +.B limnoria-reset-password .RI [ options ] " users.conf .SH DESCRIPTION -.B supybot-reset-password -changes a user's password in a Supybot users.conf file +.B limnoria-reset-password +changes a user's password in a Limnoria users.conf file .SH OPTIONS .TP .B \-\^\-version @@ -25,11 +25,11 @@ Specifies the new password to use for the new user. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-adduser (1) +.IR limnoria (1), +.IR limnoria-adduser (1) .SH AUTHOR This manual page was originally written by Valentin Lorentz . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-test.1 limnoria-2023.5.27/man/supybot-test.1 --- limnoria-2023.1.28/man/supybot-test.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-test.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,15 +1,15 @@ .\" Process this file with -.\" groff -man -Tascii supybot-test.1 +.\" groff -man -Tascii limnoria-test.1 .\" -.TH SUPYBOT-TEST 1 "OCTOBER 2005" +.TH LIMNORIA-TEST 1 "OCTOBER 2005" .SH NAME -supybot-test \- Runs the test suite for a Supybot plugin +limnoria-test \- Runs the test suite for a Limnoria plugin .SH SYNOPSIS -.B supybot-test +.B limnoria-test .RI [ options ] " plugins .SH DESCRIPTION -.B supybot-test -Runs the test suite for a Supybot plugin +.B limnoria-test +Runs the test suite for a Limnoria plugin .SH OPTIONS .TP .B \-\^\-version @@ -38,14 +38,14 @@ Looks in the given directory for plugins and loads the tests for all of them. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-botchk (1), -.IR supybot-wizard (1), -.IR supybot-adduser (1), -.IR supybot-plugin-doc (1), -.IR supybot-plugin-create (1) +.IR limnoria (1), +.IR limnoria-botchk (1), +.IR limnoria-wizard (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/man/supybot-wizard.1 limnoria-2023.5.27/man/supybot-wizard.1 --- limnoria-2023.1.28/man/supybot-wizard.1 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/man/supybot-wizard.1 2023-05-18 13:06:27.000000000 +0000 @@ -1,17 +1,17 @@ .\" Process this file with -.\" groff -man -Tascii supybot-wizard.1 +.\" groff -man -Tascii limnoria-wizard.1 .\" -.TH SUPYBOT-WIZARD 1 "SEPTEMBER 2004" +.TH LIMNORIA-WIZARD 1 "SEPTEMBER 2004" .SH NAME -supybot-wizard \- A wizard for creating Supybot configuration files +limnoria-wizard \- A wizard for creating Limnoria configuration files .SH SYNOPSIS -.B supybot-wizard +.B limnoria-wizard .RI [ options ] .SH DESCRIPTION -.B supybot-wizard +.B limnoria-wizard is an in-depth wizard that provides a nice user interface for creating configuration files for -.IR supybot (1). +.IR limnoria (1). .SH OPTIONS .TP .B \-\^\-version @@ -29,14 +29,14 @@ connection. .SH "SEE ALSO" .IR python (1), -.IR supybot (1), -.IR supybot-test (1), -.IR supybot-botchk (1), -.IR supybot-adduser (1), -.IR supybot-plugin-doc (1), -.IR supybot-plugin-create (1) +.IR limnoria (1), +.IR limnoria-test (1), +.IR limnoria-botchk (1), +.IR limnoria-adduser (1), +.IR limnoria-plugin-doc (1), +.IR limnoria-plugin-create (1) .SH AUTHOR This manual page was originally written by James McCoy . Permission is granted to copy, -distribute and/or modify this document under the terms of the Supybot +distribute and/or modify this document under the terms of the Limnoria license, a BSD-style license. diff -Nru limnoria-2023.1.28/PKG-INFO limnoria-2023.5.27/PKG-INFO --- limnoria-2023.1.28/PKG-INFO 2023-02-11 09:15:17.752235200 +0000 +++ limnoria-2023.5.27/PKG-INFO 2023-05-27 06:02:35.591968800 +0000 @@ -1,12 +1,11 @@ Metadata-Version: 2.1 Name: limnoria -Version: 2023.1.28 +Version: 2023.5.27 Summary: A multipurpose Python IRC bot, designed for flexibility and robustness , while being easy to install, set up, and maintain. Home-page: https://limnoria.net/ +Download-URL: https://pypi.python.org/pypi/limnoria Author: Valentin Lorentz Author-email: progval+limnoria@progval.net -License: UNKNOWN -Download-URL: https://pypi.python.org/pypi/limnoria Platform: linux Platform: linux2 Platform: win32 @@ -31,10 +30,11 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: Communications :: Chat :: Internet Relay Chat Classifier: Topic :: Software Development :: Libraries :: Python Modules Provides: supybot License-File: LICENSE.md A robust, full-featured Python IRC bot with a clean and flexible plugin API. Equipped with a complete ACL system for specifying user permissions with as much as per-command granularity. Batteries are included in the form of numerous plugins already written. - diff -Nru limnoria-2023.1.28/scripts/supybot limnoria-2023.5.27/scripts/supybot --- limnoria-2023.1.28/scripts/supybot 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot 1970-01-01 00:00:00.000000000 +0000 @@ -1,372 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2003-2004, Jeremiah Fincher -# Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -""" -This is the main program to run Supybot. -""" - -import supybot - -import re -import os -import sys -import atexit -import shutil -import signal - -from io import StringIO # Import this after version check since this will fail on Python 2 - -def _termHandler(signalNumber, stackFrame): - raise SystemExit('Signal #%s.' % signalNumber) - -signal.signal(signal.SIGTERM, _termHandler) - -import time -import optparse -import textwrap - -started = time.time() - -import supybot -import supybot.utils as utils -import supybot.registry as registry -import supybot.questions as questions -import supybot.ircutils as ircutils -try: - import supybot.i18n as i18n -except ImportError: - sys.stderr.write("""Error: - You are running a mix of Limnoria and stock Supybot code. Although you run - one of Limnoria\'s executables, Python tries to load stock - Supybot\'s library. To fix this issue, uninstall Supybot - ("%s -m pip uninstall supybot" should do the job) - and install Limnoria again. - For your information, Supybot's libraries are installed here: - %s\n""" % - (sys.executable, '\n '.join(supybot.__path__))) - exit(-1) - -from supybot.version import version - -def main(): - import supybot.conf as conf - import supybot.world as world - import supybot.drivers as drivers - import supybot.schedule as schedule - # We schedule this event rather than have it actually run because if there - # is a failure between now and the time it takes the Owner plugin to load - # all the various plugins, our registry file might be wiped. That's bad. - interrupted = False - when = conf.supybot.upkeepInterval() - schedule.addPeriodicEvent(world.upkeep, when, name='upkeep', now=False) - world.startedAt = started - while world.ircs: - try: - drivers.run() - except KeyboardInterrupt: - if interrupted: - # Interrupted while waiting for queues to clear. Let's clear - # them ourselves. - for irc in world.ircs: - irc._reallyDie() - continue - else: - interrupted = True - log.info('Exiting due to Ctrl-C. ' - 'If the bot doesn\'t exit within a few seconds, ' - 'feel free to press Ctrl-C again to make it exit ' - 'without flushing its message queues.') - world.upkeep() - for irc in world.ircs: - quitmsg = conf.supybot.plugins.Owner.quitMsg() or \ - 'Ctrl-C at console.' - # Because we're quitting from the console, none of the - # standard msg substitutions exist, and these will show as - # raw strings by default. Substitute them here with - # something meaningful instead. - env = dict((key, '') - for key in ('who', 'nick', 'user', 'host')) - quitmsg = ircutils.standardSubstitute(irc, None, quitmsg, - env=env) - irc.queueMsg(ircmsgs.quit(quitmsg)) - irc.die() - except SystemExit as e: - s = str(e) - if s: - log.info('Exiting due to %s', s) - break - except: - try: # Ok, now we're *REALLY* paranoid! - log.exception('Exception raised out of drivers.run:') - except Exception as e: - print('Exception raised in log.exception. This is *really*') - print('bad. Hopefully it won\'t happen again, but tell us') - print('about it anyway, this is a significant problem.') - print('Anyway, here\'s the exception: %s' % \ - utils.gen.exnToString(e)) - except: - print('Oh, this really sucks. Not only did log.exception') - print('raise an exception, but freaking-a, it was a string') - print('exception. People who raise string exceptions should') - print('die a slow, painful death.') - httpserver.stopServer() - now = time.time() - seconds = now - world.startedAt - log.info('Total uptime: %s.', utils.gen.timeElapsed(seconds)) - (user, system, _, _, _) = os.times() - log.info('Total CPU time taken: %.2f seconds.', user+system) - log.info('No more Irc objects, exiting.') - -if __name__ == '__main__': - parser = optparse.OptionParser(usage='Usage: %prog [options] configFile', - version='Limnoria %s running on Python %s' % - (version, sys.version)) - parser.add_option('-P', '--profile', action='store_true', dest='profile', - help='enables profiling') - parser.add_option('-n', '--nick', action='store', - dest='nick', default='', - help='nick the bot should use') - parser.add_option('-u', '--user', action='store', - dest='user', default='', - help='full username the bot should use') - parser.add_option('-i', '--ident', action='store', - dest='ident', default='', - help='ident the bot should use') - parser.add_option('-d', '--daemon', action='store_true', - dest='daemon', - help='Determines whether the bot will daemonize. ' - 'This is a no-op on non-POSIX systems.') - parser.add_option('', '--allow-default-owner', action='store_true', - dest='allowDefaultOwner', - help='Determines whether the bot will allow its ' - 'defaultCapabilities not to include "-owner", thus ' - 'giving all users the owner capability by default. ' - 'This is a security risk since it allows anyone to run ' - 'any command on your bot, so we advise not to use this.') - parser.add_option('', '--allow-root', action='store_true', - dest='allowRoot', - help='Determines whether the bot will be allowed to run ' - 'as root. This should not be used except in special ' - 'circumstances, such as running inside a containerized ' - 'environment.') - parser.add_option('', '--debug', action='store_true', dest='debug', - help='Determines whether some extra debugging stuff ' - 'will be logged in this script.') - parser.add_option('', '--disable-multiprocessing', action='store_true', - dest='disableMultiprocessing', - help='Disables multiprocessing stuff. May lead to ' - 'vulnerabilities.') - - (options, args) = parser.parse_args() - - if os.name == 'posix': - if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot: - sys.stderr.write('Running as root is not supported by default (see --allow-root).') - sys.stderr.write(os.linesep) - sys.exit(-1) - - if len(args) > 1: - parser.error("""Only one configuration file should be specified.""") - elif not args: - parser.error(utils.str.normalizeWhitespace("""It seems you've given me - no configuration file. If you do have a configuration file, be sure to - specify the filename. If you don't have a configuration file, read - docs/GETTING_STARTED and follow the instructions.""")) - else: - registryFilename = args.pop() - try: - # The registry *MUST* be opened before importing log or conf. - registry.open_registry(registryFilename) - shutil.copyfile(registryFilename, registryFilename + '.bak') - except registry.InvalidRegistryFile as e: - s = '%s in %s. Please fix this error and start supybot again.' % \ - (e, registryFilename) - s = textwrap.fill(s) - sys.stderr.write(s) - sys.stderr.write(os.linesep) - raise - sys.exit(-1) - except EnvironmentError as e: - sys.stderr.write(str(e)) - sys.stderr.write(os.linesep) - sys.exit(-1) - - i18n.getLocaleFromRegistryCache() - - try: - import supybot.log as log - except supybot.registry.InvalidRegistryValue as e: - # This is raised here because supybot.log imports supybot.conf. - name = e.value._name - errmsg = textwrap.fill('%s: %s' % (name, e), - width=78, subsequent_indent=' '*len(name)) - sys.stderr.write(errmsg) - sys.stderr.write(os.linesep) - sys.stderr.write('Please fix this error in your configuration file ' - 'and restart your bot.') - sys.stderr.write(os.linesep) - sys.exit(-1) - import supybot.conf as conf - import supybot.world as world - i18n.import_conf() - world.starting = True - - def closeRegistry(): - # We only print if world.dying so we don't see these messages during - # upkeep. - logger = log.debug - if world.dying: - logger = log.info - logger('Writing registry file to %s', registryFilename) - registry.close(conf.supybot, registryFilename) - logger('Finished writing registry file.') - world.flushers.append(closeRegistry) - world.registryFilename = registryFilename - - nick = options.nick or conf.supybot.nick() - user = options.user or conf.supybot.user() - ident = options.ident or conf.supybot.ident() - - networks = conf.supybot.networks() - if not networks: - questions.output("""No networks defined. Perhaps you should re-run the - wizard?""", fd=sys.stderr) - # XXX We should turn off logging here for a prettier presentation. - sys.exit(-1) - - if os.name == 'posix' and options.daemon: - def fork(): - child = os.fork() - if child != 0: - if options.debug: - print('Parent exiting, child PID: %s' % child) - # We must us os._exit instead of sys.exit so atexit handlers - # don't run. They shouldn't be dangerous, but they're ugly. - os._exit(0) - fork() - os.setsid() - # What the heck does this do? I wonder if it breaks anything... - # ...It did. I don't know why, but it seems largely useless. It seems - # to me reasonable that we should respect the user's umask. - #os.umask(0) - # Let's not do this for now (at least until I can make sure it works): - # Actually, let's never do this -- we'll always have files open in the - # bot directories, so they won't be able to be unmounted anyway. - # os.chdir('/') - fork() - # Since this is the indicator that no writing should be done to stdout, - # we'll set it to True before closing stdout et alii. - conf.daemonized = True - # Closing stdin shouldn't cause problems. We'll let it raise an - # exception if it does. - sys.stdin.close() - # Closing these two might cause problems; we log writes to them as - # level WARNING on upkeep. - sys.stdout.close() - sys.stderr.close() - sys.stdout = StringIO() - sys.stderr = StringIO() - # We have to be really methodical here. - os.close(0) - os.close(1) - os.close(2) - fd = os.open('/dev/null', os.O_RDWR) - os.dup2(fd, 0) - os.dup2(fd, 1) - os.dup2(fd, 2) - signal.signal(signal.SIGHUP, signal.SIG_IGN) - log.info('Completed daemonization. Current PID: %s', os.getpid()) - - # Stop setting our own umask. See comment above. - #os.umask(077) - - # Let's write the PID file. This has to go after daemonization, obviously. - pidFile = conf.supybot.pidFile() - if pidFile: - try: - fd = open(pidFile, 'w') - pid = os.getpid() - fd.write('%s%s' % (pid, os.linesep)) - fd.close() - def removePidFile(): - try: - os.remove(pidFile) - except EnvironmentError as e: - log.error('Could not remove pid file: %s', e) - atexit.register(removePidFile) - except EnvironmentError as e: - log.critical('Error opening/writing pid file %s: %s', pidFile, e) - sys.exit(-1) - - conf.allowDefaultOwner = options.allowDefaultOwner - world.disableMultiprocessing = options.disableMultiprocessing - - if not os.path.exists(conf.supybot.directories.log()): - os.mkdir(conf.supybot.directories.log()) - if not os.path.exists(conf.supybot.directories.conf()): - os.mkdir(conf.supybot.directories.conf()) - if not os.path.exists(conf.supybot.directories.data()): - os.mkdir(conf.supybot.directories.data()) - if not os.path.exists(conf.supybot.directories.data.tmp()): - os.mkdir(conf.supybot.directories.tmp()) - - userdataFilename = os.path.join(conf.supybot.directories.conf(), - 'userdata.conf') - # Let's open this now since we've got our directories setup. - if not os.path.exists(userdataFilename): - fd = open(userdataFilename, 'w') - fd.write('\n') - fd.close() - registry.open_registry(userdataFilename) - - import supybot.irclib as irclib - import supybot.ircmsgs as ircmsgs - import supybot.drivers as drivers - import supybot.callbacks as callbacks - import supybot.plugins.Owner as Owner - - # These may take some resources, and it does not need to be run while boot, so - # we import it as late as possible (but before plugins are loaded). - import supybot.httpserver as httpserver - - owner = Owner.Class() - - if options.profile: - import profile - world.profiling = True - profile.run('main()', '%s-%i.prof' % (nick, time.time())) - else: - main() - - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/scripts/supybot-adduser limnoria-2023.5.27/scripts/supybot-adduser --- limnoria-2023.1.28/scripts/supybot-adduser 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-adduser 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2002-2004, Jeremiah Fincher -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - - - -import supybot - -from supybot.questions import * - -import os -import sys -import optparse - -def main(): - import supybot.log as log - import supybot.conf as conf - conf.supybot.log.stdout.setValue(False) - parser = optparse.OptionParser(usage='Usage: %prog [options] ', - version='supybot %s' % conf.version) - parser.add_option('-u', '--username', action='store', default='', - dest='name', - help='username for the user.') - parser.add_option('-p', '--password', action='store', default='', - dest='password', - help='password for the user.') - parser.add_option('-c', '--capability', action='append', - dest='capabilities', metavar='CAPABILITY', - help='capability the user should have; ' - 'this option may be given multiple times.') - (options, args) = parser.parse_args() - if len(args) != 1: - parser.error('Specify the users.conf file you\'d like to use. ' - 'Be sure *not* to specify your registry file, generated ' - 'by supybot-wizard. This is not the file you want. ' - 'Instead, take a look in your conf directory (usually ' - 'named "conf") and take a gander at the file ' - '"users.conf". That\'s the one you want.') - - filename = os.path.abspath(args[0]) - conf.supybot.directories.log.setValue('/') - conf.supybot.directories.conf.setValue('/') - conf.supybot.directories.data.setValue('/') - conf.supybot.directories.plugins.setValue(['/']) - conf.supybot.databases.users.filename.setValue(filename) - import supybot.ircdb as ircdb - - if not options.name: - name = '' - while not name: - name = something('What is the user\'s name?') - try: - # Check to see if the user is already in the database. - _ = ircdb.users.getUser(name) - # Uh oh. That user already exists; - # otherwise we'd have KeyError'ed. - output('That user already exists. Try another name.') - name = '' - except KeyError: - # Good. No such user exists. We'll pass. - pass - else: - try: - # Same as above. We exit here instead. - _ = ircdb.users.getUser(options.name) - output('That user already exists. Try another name.') - sys.exit(-1) - except KeyError: - name = options.name - - if not options.password: - password = getpass('What is %s\'s password? ' % name) - else: - password = options.password - - if not options.capabilities: - capabilities = [] - prompt = 'Would you like to give %s a capability?' % name - while yn(prompt): - capabilities.append(anything('What capability?')) - prompt = 'Would you like to give %s another capability?' % name - else: - capabilities = options.capabilities - - user = ircdb.users.newUser() - user.name = name - user.setPassword(password) - for capability in capabilities: - user.addCapability(capability) - ircdb.users.setUser(user) - ircdb.users.flush() - #os.system('cat %s' % filename) # Was this here just for debugging? - ircdb.users.close() - print('User %s added.' % name) - -if __name__ == '__main__': - try: - main() - except KeyboardInterrupt: - pass - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/scripts/supybot-botchk limnoria-2023.5.27/scripts/supybot-botchk --- limnoria-2023.1.28/scripts/supybot-botchk 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-botchk 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2005, Jeremiah Fincher -# Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -VERBOSE = False - -def readPid(filename): - fd = open(filename) - try: - return int(fd.read().strip()) - finally: - fd.close() - -def isAlive(pid): - try: - os.kill(pid, 0) - return True - except OSError: - return False - -def debug(s): - if VERBOSE: - if not s.endswith(os.linesep): - s += os.linesep - sys.stdout.write(s) - -if __name__ == '__main__': - # XXX I wanted this for conf.version, but this will create directories. We - # really need to refactor conf so it either doesn't create directories, or - # so that static information (like the version) can be imported from - # somewhere else. - # import supybot.conf as conf - import os - import sys - import optparse - import subprocess - - parser = optparse.OptionParser(usage='Usage: %prog [options]') - parser.add_option('', '--verbose', action='store_true', - help='Makes output verbose.') - parser.add_option('', '--botdir', - help='Determines what directory the bot resides in and ' - 'should be started from.') - parser.add_option('', '--pidfile', - help='Determines what file to look in for the pid of ' - 'the running bot. This should be relative to the ' - 'given bot directory. Note that for this to actually ' - 'work, you have to make a matching entry in the ' - 'supybot.pidFile config in the supybot registry.') - parser.add_option('', '--supybot', default='supybot', - help='Determines where the supybot executable is ' - 'located. If not given, assumes that supybot is ' - 'in $PATH.') - parser.add_option('', '--conffile', - help='Determines what configuration file should be ' - 'given to the supybot executable when (re)starting the ' - 'bot.') - - (options, args) = parser.parse_args() - VERBOSE = options.verbose - - if args: - parser.error('Extra arguments given.') - if not options.botdir: - parser.error('No botdir given.') - if not options.pidfile: - parser.error('No pidfile given.') - if not options.conffile: - parser.error('No conffile given.') - - os.chdir(options.botdir) - open(options.pidfile, 'a').close() - - pid = None - try: - pid = readPid(options.pidfile) - debug('Found pidFile with proper pid contents of %s' % pid) - except ValueError as e: - foundBot = False - - if pid is not None: - foundBot = isAlive(pid) - if foundBot: - debug('Pid %s is alive and belongs to us.' % pid) - else: - debug('Pid %s is not the bot.' % pid) - - if not foundBot: - # First, we check if the pidfile is writable. If not, supybot will just exit, - # so we go ahead and refuse to start it. - try: - open(options.pidfile, 'r+') - except EnvironmentError as e: - debug('pidfile (%s) is not writable: %s' % (options.pidfile, e)) - sys.exit(-1) - debug('Bot not found, starting.') - cmdline = [options.supybot, '--daemon', options.conffile] - inst = subprocess.Popen(cmdline, close_fds=True, - stderr=subprocess.STDOUT, - stdin=None, stdout=subprocess.PIPE) - debug('Output from supybot: %r' % inst.stdout.read()) - ret = inst.wait() - debug('Bot started, command line %r returned %s.' % (' '.join(cmdline), - ret)) - sys.exit(ret) - else: - sys.exit(0) - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/scripts/supybot-plugin-create limnoria-2023.5.27/scripts/supybot-plugin-create --- limnoria-2023.1.28/scripts/supybot-plugin-create 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-plugin-create 1970-01-01 00:00:00.000000000 +0000 @@ -1,319 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2002-2005, Jeremiah Fincher -# Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -from __future__ import print_function - -import supybot - -import os -import sys -import time -import os.path -import optparse - -def error(s): - sys.stderr.write(textwrap.fill(s)) - sys.stderr.write(os.linesep) - sys.exit(-1) - -import supybot.conf as conf -from supybot.questions import * - -copyright = ''' -### -# Copyright (c) %s, %%s -# All rights reserved. -# -%%s -### -''' % time.strftime('%Y') -# Here we use strip() instead of lstrip() on purpose. -copyright = copyright.strip() - -license = ''' -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -''' -license = license.lstrip() - -pluginTemplate = ''' -%s - -from supybot import utils, plugins, ircutils, callbacks -from supybot.commands import * -from supybot.i18n import PluginInternationalization - - -_ = PluginInternationalization('%s') - - -class %s(callbacks.Plugin): - """%s""" - %s - - -Class = %s - - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: -'''.lstrip() # This removes the newlines that precede and follow the text. - -configTemplate = ''' -%s - -from supybot import conf, registry -try: - from supybot.i18n import PluginInternationalization - _ = PluginInternationalization('%s') -except: - # Placeholder that allows to run the plugin on a bot - # without the i18n module - _ = lambda x: x - - -def configure(advanced): - # This will be called by supybot to configure this module. advanced is - # a bool that specifies whether the user identified themself as an advanced - # user or not. You should effect your configuration by manipulating the - # registry as appropriate. - from supybot.questions import expect, anything, something, yn - conf.registerPlugin(%r, True) - - -%s = conf.registerPlugin(%r) -# This is where your configuration variables (if any) should go. For example: -# conf.registerGlobalValue(%s, 'someConfigVariableName', -# registry.Boolean(False, _("""Help for someConfigVariableName."""))) - - -# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: -'''.lstrip() - - -__init__Template = ''' -%s - -""" -%s: %s -""" - -import sys -import supybot -from supybot import world - -# Use this for the version of this plugin. -__version__ = "" - -# XXX Replace this with an appropriate author or supybot.Author instance. -__author__ = supybot.authors.unknown - -# This is a dictionary mapping supybot.Author instances to lists of -# contributions. -__contributors__ = {} - -# This is a url where the most recent plugin package can be downloaded. -__url__ = '' - -from . import config -from . import plugin -from importlib import reload -# In case we're being reloaded. -reload(config) -reload(plugin) -# Add more reloads here if you add third-party modules and want them to be -# reloaded when this plugin is reloaded. Don't forget to import them as well! - -if world.testing: - from . import test - -Class = plugin.Class -configure = config.configure - - -# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: -'''.lstrip() - -setupTemplate = ''' -%s - -from supybot.setup import plugin_setup - -plugin_setup( - %r, -) -'''.lstrip() - -testTemplate = ''' -%s - -from supybot.test import * - - -class %sTestCase(PluginTestCase): - plugins = (%r,) - - -# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: -'''.lstrip() - -readmeTemplate = ''' -%s -'''.lstrip() - -def main(): - global copyright - global license - parser = optparse.OptionParser(usage='Usage: %prog [options]', - version='Supybot %s' % conf.version) - parser.add_option('-n', '--name', action='store', dest='name', - help='sets the name for the plugin.') - parser.add_option('-t', '--thread', action='store_true', dest='threaded', - help='makes the plugin threaded.') - parser.add_option('-a', '--author', '--real-name', action='store', - dest='realName', help='Determines who the copyright is ' - 'assigned to.') - parser.add_option('-d', '--desc', action='store', dest='desc', - help='Short description of plugin.') - (options, args) = parser.parse_args() - if options.name: - name = options.name - threaded = options.threaded - else: - name = something('What should the name of the plugin be?') - if name.endswith('.py'): - name = name[:-3] - while name[0].islower(): - print('Plugin names must begin with a capital letter.') - name = something('What should the name of the plugin be?') - if name.endswith('.py'): - name = name[:-3] - - if os.path.exists(name): - error('A file or directory named %s already exists; remove or ' - 'rename it and run this program again.' % name) - - print(textwrap.fill(textwrap.dedent(""" - Sometimes you'll want a callback to be threaded. If its methods - (command or regexp-based, either one) will take a significant amount - of time to run, you'll want to thread them so they don't block the - entire bot.""").strip())) - print() - threaded = yn('Does your plugin need to be threaded?') - - if options.realName: - realName = options.realName - else: - realName = something(textwrap.dedent(""" - What is your name, so I can fill in the copyright and license - appropriately? - """).strip()) - - if not yn('Do you wish to use Supybot\'s license for your plugin?'): - license = '#' - - if not options.desc: - options.desc = something(textwrap.dedent(""" - Please provide a short description of the plugin: - """).strip()) - - if threaded: - threaded = 'threaded = True' - else: - threaded = 'pass' - if name.endswith('.py'): - name = name[:-3] - while name[0].islower(): - print('Plugin names must begin with a capital letter.') - name = something('What should the name of the plugin be?') - if name.endswith('.py'): - name = name[:-3] - copyright %= (realName, license) - pathname = name - - # Make the directory. - os.mkdir(pathname) - - def writeFile(filename, s): - fd = open(os.path.join(pathname, filename), 'w') - try: - fd.write(s) - finally: - fd.close() - - writeFile('plugin.py', pluginTemplate % (copyright, name, name, - options.desc, threaded, name)) - writeFile('config.py', configTemplate % (copyright, name, name, name, name, - name)) - writeFile('__init__.py', __init__Template % (copyright, name, options.desc)) - writeFile('setup.py', setupTemplate % (copyright, name)) - writeFile('test.py', testTemplate % (copyright, name, name)) - writeFile('README.md', readmeTemplate % (options.desc,)) - - pathname = os.path.join(pathname, 'local') - os.mkdir(pathname) - writeFile('__init__.py', - '# Stub so local is a module, used for third-party modules\n') - - print('Your new plugin template is in the %s directory.' % name) - -if __name__ == '__main__': - try: - main() - except KeyboardInterrupt: - print() - output("""It looks like you cancelled out of this script before it was - finished. Obviously, nothing was written, but just run this script - again whenever you want to generate a template for a plugin.""") - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/scripts/supybot-plugin-doc limnoria-2023.5.27/scripts/supybot-plugin-doc --- limnoria-2023.1.28/scripts/supybot-plugin-doc 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-plugin-doc 1970-01-01 00:00:00.000000000 +0000 @@ -1,352 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2005, Ali Afshar -# Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -import os -import sys -import shutil -import string -import logging - -import supybot - -def error(s): - sys.stderr.write('%s\n' % s) - sys.exit(-1) - -# We need to do this before we import conf. -if not os.path.exists('doc-conf'): - os.mkdir('doc-conf') - -registryFilename = os.path.join('doc-conf', 'doc.conf') -try: - fd = open(registryFilename, 'w') - fd.write(""" -supybot.directories.data: doc-data -supybot.directories.conf: doc-conf -supybot.directories.log: doc-logs -supybot.log.stdout: False -supybot.log.level: DEBUG -supybot.log.format: %(levelname)s %(message)s -supybot.log.plugins.individualLogfiles: False -supybot.databases: sqlite3 anydbm cdb flat pickle -""") - fd.close() -except EnvironmentError as e: - error('Unable to open %s for writing.' % registryFilename) - -import supybot.registry as registry -registry.open_registry(registryFilename) - -import supybot.log as log -import supybot.conf as conf -conf.supybot.flush.setValue(False) - -import textwrap - -import supybot.utils as utils -import supybot.world as world -import supybot.plugin as plugin -import supybot.registry as registry - -world.documenting = True - -TITLE_TEMPLATE = 'Documentation for the $name plugin for Supybot' - -class PluginDoc(object): - def __init__(self, mod, titleTemplate): - self.mod = mod - self.inst = self.mod.Class(None) - self.name = self.inst.name() - self.appendExtraBlankLine = False - self.titleTemplate = string.Template(titleTemplate) - self.lines = [] - - def appendLine(self, line, indent=0): - line = line.strip() - indent = ' ' * indent - # this looked like a good idea, but it's actually breaking lines - # in the middle of ReST elements: - #lines = textwrap.wrap(line, 79, - # initial_indent=indent, - # subsequent_indent=indent) - lines = [indent + line.replace('\n', '\n' + indent)] - self.lines.extend(lines) - if self.appendExtraBlankLine: - self.lines.append('') - - def renderRST(self): - self.appendExtraBlankLine = False - self.appendLine('.. _plugin-%s:' % self.name) - self.lines.append('') - s = self.titleTemplate.substitute(name=self.name) - self.appendLine(s) - self.appendLine('=' * len(s)) - self.lines.append('') - self.appendLine('Purpose') - self.appendLine('-------') - self.lines.append('') - pdoc = getattr(self.mod, '__doc__', - 'My author didn\'t give me a purpose.') - self.appendLine(pdoc) - self.lines.append('') - cdoc = getattr(self.mod.Class, '__doc__', None) - if cdoc is not None: - self.appendLine('Usage') - self.appendLine('-----') - self.lines.append('') - # We add spaces at the beginning in case the docstring does not - # start with a newline (the default, unfortunately) - self.appendLine(textwrap.dedent(" "* 1000 + cdoc).lstrip()) - self.lines.append('') - commands = self.inst.listCommands() - if len(commands): - self.lines.append('.. _commands-%s:\n' % self.name) - self.appendLine('Commands') - self.appendLine('--------') - self.lines.append('') - for command in commands: - log.debug('command: %s', command) - self.lines.append('.. _command-%s-%s:\n' % - (self.name.lower(), command.replace(' ', '.'))) - line = '%s ' % command - command = command.split() - doc = self.inst.getCommandHelp(command) - if doc: - doc = doc.replace('\x02', '') - (args, help) = doc.split(')', 1) - args = args.split('(', 1)[1] - args = args[len(' '.join(command)):].strip() - help = help.split('--', 1)[1].strip() - self.appendLine(line + args) - self.appendLine(help, 1) - else: - self.appendLine('No help associated with this command') - self.lines.append('') - # now the config - try: - confs = conf.supybot.plugins.get(self.name) - self.lines.append('.. _conf-%s:\n' % self.name) - self.appendLine('Configuration') - self.appendLine('-------------') - self.lines.append('') - except registry.NonExistentRegistryEntry: - log.info('No configuration for plugin %s', plugin) - self.appendLine('No configuration for this plugin') - else: - for confValues in self.genConfig(confs, 0): - (name, kind, help, default, indent) = confValues - self.appendLine('.. _conf-%s:' % name, indent - 1) - self.lines.append('\n') - self.appendLine('%s' % name, indent - 1) - if default: - self.appendLine( - ('This config variable defaults to %s, %s') - % (default, kind), indent) - else: - self.appendLine('This %s' % kind, indent) - if help: - self.lines.append('') - self.appendLine(help, indent) - self.lines.append('') - return '\n'.join(self.lines) + '\n' - - def renderSTX(self): - self.appendExtraBlankLine = True - self.appendLine(self.titleTemplate.substitute(name=self.name)) - self.appendLine('Purpose', 1) - pdoc = getattr(self.mod, '__doc__', - 'My author didn\'t give me a purpose.') - self.appendLine(pdoc, 2) - cdoc = getattr(self.mod.Class, '__doc__', None) - if cdoc is not None: - self.appendLine('Usage', 1) - self.appendLine(cdoc, 2) - commands = self.inst.listCommands() - if len(commands): - self.appendLine('Commands', 1) - for command in commands: - log.debug('command: %s', command) - line = '* %s ' % command - command = command.split() - doc = self.inst.getCommandHelp(command) - if doc: - doc = doc.replace('\x02', '') - (args, help) = doc.split(')', 1) - args = args.split('(', 1)[1] - args = args[len(' '.join(command)):].strip() - help = help.split('--', 1)[1].strip() - self.appendLine(line + args, 2) - self.appendLine(help, 3) - else: - self.appendLine('No help associated with this command', 3) - # now the config - try: - confs = conf.supybot.plugins.get(self.name) - self.appendLine('Configuration', 1) - except registry.NonExistentRegistryEntry: - log.info('No configuration for plugin %s', plugin) - self.appendLine('No configuration for this plugin', 2) - else: - for confValues in self.genConfig(confs, 2): - (name, kind, help, default, indent) = confValues - self.appendLine('* %s' % name, indent - 1) - if default: - self.appendLine( - ('This config variable defaults to %s, %s') - % (default, kind), indent) - else: - self.appendLine('This %s' % kind, indent) - self.appendLine(help, indent) - return '\n'.join(self.lines) + '\n' - - def genConfig(self, item, origindent): - confVars = item.getValues(getChildren=False, fullNames=False) - if not confVars: - return - for (c, v) in confVars: - name = v._name - indent = origindent + 1 - if isinstance(v, registry.Value): - try: - default = utils.str.dqrepr(str(v)) - help = v.help() - except registry.NonExistentRegistryEntry: - pass - else: - cv = '' if v._channelValue else ' not' - nv = '' if v._networkValue else ' not' - kind = ( - 'is%s network-specific, and is%s channel-specific.' - % (nv, cv) - ) - else: - help = '' - default = '' - kind = 'is a group of:' - yield (name, kind, help, default, indent) - for confValues in self.genConfig(v, indent): - yield confValues - -def genDoc(m, options): - Plugin = PluginDoc(m, options.titleTemplate) - print('Generating documentation for %s...' % Plugin.name) - outputFilename = string.Template(options.outputFilename).substitute( - name=Plugin.name, format=options.format) - path = os.path.join(options.outputDir, outputFilename) - try: - fd = open(path, 'w') - except EnvironmentError as e: - error('Unable to open %s for writing.' % path) - f = getattr(Plugin, 'render%s' % options.format.upper(), None) - if f is None: - fd.close() - error('Unknown render format: `%s\'' % options.format) - try: - fd.write(f()) - finally: - fd.close() - -if __name__ == '__main__': - import glob - import os.path - import optparse - import supybot.plugin as plugin - - parser = optparse.OptionParser(usage='Usage: %prog [options] [plugins]', - version='Supybot %s' % conf.version) - parser.add_option('-c', '--clean', action='store_true', default=False, - dest='clean', help='Cleans the various data/conf/logs ' - 'directories after generating the docs.') - parser.add_option('-o', '--output-dir', dest='outputDir', default='.', - help='Specifies the directory in which to write the ' - 'documentation for the plugin.') - parser.add_option('--output-filename', dest='outputFilename', - default='$name.$format', - help='Specifies the path of individual output files. ' - 'If present in the value, "$name" will be substituted ' - 'with the plugin\'s name and "$format" with the value ' - 'if --format.') - parser.add_option('-f', '--format', dest='format', choices=['rst', 'stx'], - default='rst', help='Specifies which output format to ' - 'use.') - parser.add_option('--plugins-dir', - action='append', dest='pluginsDirs', default=[], - help='Looks in in the given directory for plugins and ' - 'generates documentation for all of them.') - parser.add_option('--title-template', - default=TITLE_TEMPLATE, dest='titleTemplate', - help='Template string for the title of generated files') - (options, args) = parser.parse_args() - - # This must go before checking for args, of course. - for pluginDir in options.pluginsDirs: - for name in glob.glob(os.path.join(pluginDir, '*')): - if os.path.isdir(name): - args.append(name) - - if not args: - parser.print_help() - sys.exit(-1) - - args = [s.rstrip('\\/') for s in args] - pluginDirs = set([os.path.dirname(s) or '.' for s in args]) - conf.supybot.directories.plugins.setValue(list(pluginDirs)) - pluginNames = set([os.path.basename(s) for s in args]) - plugins = set([]) - for pluginName in pluginNames: - if pluginName == '__pycache__': - continue - if pluginName.endswith('.py'): - pluginName = pluginName[:-3] - try: - pluginModule = plugin.loadPluginModule(pluginName) - except ImportError as e: - s = 'Failed to load plugin %s: %s\n' \ - '(pluginDirs: %s)' % (pluginName, e, - conf.supybot.directories.plugins()) - error(s) - plugins.add(pluginModule) - - for Plugin in plugins: - genDoc(Plugin, options) - - if options.clean: - # We are about to remove the log dir; so trying to write anything - # (such as "Shutdown initiated." and friends, from atexit callbacks) - # would result in errors. - log._handler.setLevel(logging.CRITICAL) - shutil.rmtree(conf.supybot.directories.log()) - shutil.rmtree(conf.supybot.directories.conf()) - shutil.rmtree(conf.supybot.directories.data()) - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=78: diff -Nru limnoria-2023.1.28/scripts/supybot-reset-password limnoria-2023.5.27/scripts/supybot-reset-password --- limnoria-2023.1.28/scripts/supybot-reset-password 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-reset-password 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2002-2004, Jeremiah Fincher -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - - - -import supybot - -from supybot.questions import * - -import os -import sys -import optparse - -def main(): - import supybot.log as log - import supybot.conf as conf - conf.supybot.log.stdout.setValue(False) - parser = optparse.OptionParser(usage='Usage: %prog [options] ', - version='supybot %s' % conf.version) - parser.add_option('-u', '--username', action='store', default='', - dest='name', - help='username for the user.') - parser.add_option('-p', '--password', action='store', default='', - dest='password', - help='new password for the user.') - (options, args) = parser.parse_args() - if len(args) != 1: - parser.error('Specify the users.conf file you\'d like to use. ' - 'Be sure *not* to specify your registry file, generated ' - 'by supybot-wizard. This is not the file you want. ' - 'Instead, take a look in your conf directory (usually ' - 'named "conf") and take a gander at the file ' - '"users.conf". That\'s the one you want.') - - filename = os.path.abspath(args[0]) - conf.supybot.directories.log.setValue('/') - conf.supybot.directories.conf.setValue('/') - conf.supybot.directories.data.setValue('/') - conf.supybot.directories.plugins.setValue(['/']) - conf.supybot.databases.users.filename.setValue(filename) - import supybot.ircdb as ircdb - - if not options.name: - name = '' - while not name: - name = something('What is the user\'s name?') - try: - # Check to see if the user is already in the database. - _ = ircdb.users.getUser(name) - # Success! - except KeyError: - # Failure. No such user exists. Try another name. - output('That user doesn\'t exist. Try another name.') - name = '' - else: - try: - _ = ircdb.users.getUser(options.name) - name = options.name - except KeyError: - # Same as above. We exit here instead. - output('That user doesn\'t exist. Try another name.') - sys.exit(-1) - - if not options.password: - password = getpass('Please enter new password for %s: ' % name) - else: - password = options.password - - user = ircdb.users.getUser(name) - user.setPassword(password) - ircdb.users.setUser(user) - ircdb.users.flush() - ircdb.users.close() - print('User %s\'s password reset!' % name) - -if __name__ == '__main__': - try: - main() - except KeyboardInterrupt: - pass - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/scripts/supybot-test limnoria-2023.5.27/scripts/supybot-test --- limnoria-2023.1.28/scripts/supybot-test 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-test 1970-01-01 00:00:00.000000000 +0000 @@ -1,246 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2002-2005, Jeremiah Fincher -# Copyright (c) 2011, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -import os -import sys -import time -import shutil -import fnmatch -started = time.time() - -import supybot -import logging -import traceback - -# We need to do this before we import conf. -if not os.path.exists('test-conf'): - os.mkdir('test-conf') - -registryFilename = os.path.join('test-conf', 'test.conf') -fd = open(registryFilename, 'w') -fd.write(""" -supybot.directories.data: %(base_dir)s/test-data -supybot.directories.conf: %(base_dir)s/test-conf -supybot.directories.log: %(base_dir)s/test-logs -supybot.reply.whenNotCommand: True -supybot.log.stdout: False -supybot.log.stdout.level: ERROR -supybot.log.level: DEBUG -supybot.log.format: %%(levelname)s %%(message)s -supybot.log.plugins.individualLogfiles: False -supybot.protocols.irc.throttleTime: 0 -supybot.reply.whenAddressedBy.chars: @ -supybot.networks.test.server: should.not.need.this -supybot.networks.testnet1.server: should.not.need.this -supybot.networks.testnet2.server: should.not.need.this -supybot.networks.testnet3.server: should.not.need.this -supybot.nick: test -supybot.databases.users.allowUnregistration: True -""" % {'base_dir': os.getcwd()}) -fd.close() - -import supybot.registry as registry -registry.open_registry(registryFilename) - -import supybot.log as log -import supybot.conf as conf -conf.allowEval = True -conf.supybot.flush.setValue(False) - -import re -import sys -import glob -import atexit -import os.path -import unittest - -import supybot.utils as utils -import supybot.world as world -import supybot.callbacks as callbacks -world.startedAt = started - -import logging -class TestLogFilter(logging.Filter): - bads = [ - 'No callbacks in', - 'Invalid channel database', - 'Exact error', - 'Invalid user dictionary', - 'because of noFlush', - 'Queuing NICK', - 'Queuing USER', - 'IgnoresDB.reload failed', - 'Starting log for', - 'Irc object for test dying', - 'Last Irc,', - ] - def filter(self, record): - for bad in self.bads: - if bad in record.msg: - return False - return True -log._logger.addFilter(TestLogFilter()) - -class path(str): - """A class to represent platform-independent paths.""" - _r = re.compile(r'[\\/]') - def __hash__(self): - return reduce(lambda h, s: h ^ hash(s), self._r.split(self), 0) - def __eq__(self, other): - return self._r.split(self) == self._r.split(other) - -if __name__ == '__main__': - import glob - import os.path - import optparse - import supybot.test as test - import supybot.plugin as plugin - - parser = optparse.OptionParser(usage='Usage: %prog [options] [plugins]', - version='Supybot %s' % conf.version) - parser.add_option('-c', '--clean', action='store_true', default=False, - dest='clean', help='Cleans the various data/conf/logs' - 'directories before running tests.') - parser.add_option('-t', '--timeout', action='store', type='float', - dest='timeout', - help='Sets the timeout, in seconds, for tests to return ' - 'responses.') - parser.add_option('-v', '--verbose', action='count', default=0, - help='Increase verbosity, logging extra information ' - 'about each test that runs.') - parser.add_option('', '--fail-fast', action='store_true', default=False, - help='Stop at first failed test.') - parser.add_option('', '--no-network', action='store_true', default=False, - dest='nonetwork', help='Causes the network-based tests ' - 'not to run.') - parser.add_option('', '--no-setuid', action='store_true', default=False, - dest='nosetuid', help='Causes the tests based on a ' - 'setuid executable not to run.') - parser.add_option('', '--trace', action='store_true', default=False, - help='Traces all calls made. Unless you\'re really in ' - 'a pinch, you probably shouldn\'t do this; it results ' - 'in copious amounts of output.') - parser.add_option('', '--plugins-dir', - action='append', dest='pluginsDirs', default=[], - help='Looks in in the given directory for plugins and ' - 'loads the tests from all of them.') - parser.add_option('', '--exclude', - action='append', dest='excludePlugins', default=[], - help='List of plugins you do not want --plugins-dir ' - 'to include.') - parser.add_option('', '--disable-multiprocessing', action='store_true', - dest='disableMultiprocessing', - help='Disables multiprocessing stuff.') - (options, args) = parser.parse_args() - - world.disableMultiprocessing = options.disableMultiprocessing - - # This must go before checking for args, of course. - if options.excludePlugins: - exclusionPattern = re.compile('|'.join( - fnmatch.translate(pat) for pat in options.excludePlugins - )) - else: - exclusionPattern = None - for pluginDir in options.pluginsDirs: - for name in glob.glob(os.path.join(pluginDir, '*')): - if (exclusionPattern is None or not exclusionPattern.match(name)) \ - and os.path.isdir(name): - args.append(name) - - if not args: - parser.print_help() - sys.exit(-1) - - if options.timeout: - test.timeout = options.timeout - - if options.trace: - traceFilename = conf.supybot.directories.log.dirize('trace.log') - fd = open(traceFilename, 'w') - sys.settrace(utils.gen.callTracer(fd)) - atexit.register(fd.close) - atexit.register(lambda : sys.settrace(None)) - - world.myVerbose = options.verbose - - if options.nonetwork: - test.network = False - if options.nosetuid: - test.setuid = False - - log.testing = True - world.testing = True - - args = [s.rstrip('\\/') for s in args] - pluginDirs = set([os.path.dirname(s) or '.' for s in args]) - conf.supybot.directories.plugins.setValue(list(pluginDirs)) - pluginNames = set([os.path.basename(s) for s in args]) - load = unittest.defaultTestLoader.loadTestsFromModule - for pluginName in pluginNames: - if pluginName.endswith('.py'): - pluginName = pluginName[:-3] - try: - pluginModule = plugin.loadPluginModule(pluginName) - except (ImportError, callbacks.Error) as e: - sys.stderr.write('Failed to load plugin %s:' % pluginName) - traceback.print_exc() - sys.stderr.write('(pluginDirs: %s)\n' % - conf.supybot.directories.plugins()) - continue - if hasattr(pluginModule, 'test'): - test.suites.append(load(pluginModule.test)) - - suite = unittest.TestSuite(test.suites) - if options.fail_fast: - runner = unittest.TextTestRunner(verbosity=2, - failfast=True) - else: - runner = unittest.TextTestRunner(verbosity=2) - print('Testing began at %s (pid %s)' % (time.ctime(), os.getpid())) - if options.clean: - shutil.rmtree(conf.supybot.directories.log()) - log._mkDirs() - shutil.rmtree(conf.supybot.directories.conf()) - shutil.rmtree(conf.supybot.directories.data()) - result = runner.run(suite) - - if hasattr(unittest, 'asserts'): - print('Total asserts: %s' % unittest.asserts) - - if result.wasSuccessful(): - sys.exit(0) - else: - sys.exit(1) - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/scripts/supybot-wizard limnoria-2023.5.27/scripts/supybot-wizard --- limnoria-2023.1.28/scripts/supybot-wizard 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/scripts/supybot-wizard 1970-01-01 00:00:00.000000000 +0000 @@ -1,832 +0,0 @@ -#!/usr/bin/env python3 - -### -# Copyright (c) 2003-2004, Jeremiah Fincher -# Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -from __future__ import print_function - -import os -import sys - -def error(s): - sys.stderr.write(s) - if not s.endswith(os.linesep): - sys.stderr.write(os.linesep) - sys.exit(-1) - -import supybot - -import re -import time -import pydoc -import pprint -import socket -import logging -import optparse - -try: - import supybot.i18n as i18n -except ImportError: - sys.stderr.write("""Error: - You are running a mix of Limnoria and stock Supybot code. Although you run - one of Limnoria\'s executables, Python tries to load stock - Supybot\'s library. To fix this issue, uninstall Supybot - ("%s -m pip uninstall supybot" should do the job) - and install Limnoria again. - For your information, Supybot's libraries are installed here: - %s\n""" % - (sys.executable, '\n '.join(supybot.__path__))) - exit(-1) - -import supybot.ansi as ansi -import supybot.utils as utils -import supybot.ircutils as ircutils -import supybot.registry as registry -# supybot.plugin, supybot.log, and supybot.conf will be imported later, -# because we need to set a language before loading the conf - -import supybot.questions as questions -from supybot.questions import output, yn, anything, something, expect, getpass - -def getPlugins(pluginDirs): - plugins = set([]) - join = os.path.join - for pluginDir in pluginDirs: - try: - for filename in os.listdir(pluginDir): - fname = join(pluginDir, filename) - if (filename.endswith('.py') or os.path.isdir(fname)) \ - and filename[0].isupper(): - plugins.add(os.path.splitext(filename)[0]) - except OSError: - continue - plugins.discard('Owner') - plugins = list(plugins) - plugins.sort() - return plugins - -def loadPlugin(name): - import supybot.plugin as plugin - try: - module = plugin.loadPluginModule(name) - if hasattr(module, 'Class'): - return module - else: - output("""That plugin loaded fine, but didn't seem to be a real - Supybot plugin; there was no Class variable to tell us what class - to load when we load the plugin. We'll skip over it for now, but - you can always add it later.""") - return None - except Exception as e: - output("""We encountered a bit of trouble trying to load plugin %r. - Python told us %r. We'll skip over it for now, you can always add it - later.""" % (name, utils.gen.exnToString(e))) - return None - -def describePlugin(module, showUsage): - if module.__doc__: - output(module.__doc__, unformatted=False) - elif hasattr(module.Class, '__doc__'): - output(module.Class.__doc__, unformatted=False) - else: - output("""Unfortunately, this plugin doesn't seem to have any - documentation. Sorry about that.""") - if showUsage: - if hasattr(module, 'example'): - if yn('This plugin has a usage example. ' - 'Would you like to see it?', default=False): - pydoc.pager(module.example) - else: - output("""This plugin has no usage example.""") - -def clearLoadedPlugins(plugins, pluginRegistry): - for plugin in plugins: - try: - pluginKey = pluginRegistry.get(plugin) - if pluginKey(): - plugins.remove(plugin) - except registry.NonExistentRegistryEntry: - continue - -_windowsVarRe = re.compile(r'%(\w+)%') -def getDirectoryName(default, basedir=os.curdir, prompt=True): - done = False - while not done: - if prompt: - dir = something('What directory do you want to use?', - default=os.path.join(basedir, default)) - else: - dir = os.path.join(basedir, default) - orig_dir = dir - dir = os.path.expanduser(dir) - dir = _windowsVarRe.sub(r'$\1', dir) - dir = os.path.expandvars(dir) - dir = os.path.abspath(dir) - try: - os.makedirs(dir) - done = True - except OSError as e: - # 17 is File exists for Linux (and likely other POSIX systems) - # 183 is the same for Windows - if e.args[0] == 17 or (os.name == 'nt' and e.args[0] == 183): - done = True - else: - output("""Sorry, I couldn't make that directory for some - reason. The Operating System told me %s. You're going to - have to pick someplace else.""" % e) - prompt = True - return (dir, os.path.dirname(orig_dir)) - - -def main(): - import supybot.version as version - parser = optparse.OptionParser(usage='Usage: %prog [options]', - version='Supybot %s' % version.version) - parser.add_option('', '--allow-root', action='store_true', - dest='allowRoot', - help='Determines whether the wizard will be allowed to ' - 'as root. This should not be used except in special ' - 'circumstances, such as running inside a containerized ' - 'environment.') - parser.add_option('', '--allow-home', action='store_true', - dest='allowHome', - help='Determines whether the wizard will be allowed to ' - 'run directly in the HOME directory. ' - 'You should not do this unless you want it to ' - 'create multiple files in your HOME directory.') - parser.add_option('', '--allow-bin', action='store_true', - dest='allowBin', - help='Determines whether the wizard will be allowed to ' - 'run directly in a directory for binaries (eg. ' - '/usr/bin, /usr/local/bin, or ~/.local/bin). ' - 'You should not do this unless you want it to ' - 'create multiple non-binary files in directory ' - 'that should only contain binaries.') - parser.add_option('', '--no-network', action='store_false', - dest='network', - help='Determines whether the wizard will be allowed to ' - 'run without a network connection.') - (options, args) = parser.parse_args() - if os.name == 'posix': - if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot: - error('Running as root is not supported by default (see --allow-root).') - if os.name == 'posix': - if (os.getcwd() == os.path.expanduser('~')) and not options.allowHome: - error('Please, don\'t run this in your HOME directory.') - if (os.path.split(os.getcwd())[-1] == 'bin') and not options.allowBin: - error('Please, don\'t run this in a "bin" directory.') - if os.path.isfile(os.path.join('scripts', 'supybot-wizard')) or \ - os.path.isfile(os.path.join('..', 'scripts', 'supybot-wizard')): - print('') - print('+------------------------------------------------------------+') - print('| +--------------------------------------------------------+ |') - print('| | Warning: It looks like you are running the wizard from | |') - print('| | the Supybot source directory. This is not recommended. | |') - print('| | Please press Ctrl-C and change to another directory. | |') - print('| +--------------------------------------------------------+ |') - print('+------------------------------------------------------------+') - print('') - - if args: - parser.error('This program takes no non-option arguments.') - output("""This is a wizard to help you start running supybot. What it - will do is create the necessary config files based on the options you - select here. So hold on tight and be ready to be interrogated :)""") - - - output("""First of all, we can bold the questions you're asked so you can - easily distinguish the mostly useless blather (like this) from the - questions that you actually have to answer.""") - if yn('Would you like to try this bolding?', default=True): - questions.useBold = True - if not yn('Do you see this in bold?'): - output("""Sorry, it looks like your terminal isn't ANSI compliant. - Try again some other day, on some other terminal :)""") - questions.useBold = False - else: - output("""Great!""") - - ### - # Preliminary questions. - ### - output("""We've got some preliminary things to get out of the way before - we can really start asking you questions that directly relate to what your - bot is going to be like.""") - - # Advanced? - output("""We want to know if you consider yourself an advanced Supybot - user because some questions are just utterly boring and useless for new - users. Others might not make sense unless you've used Supybot for some - time.""") - advanced = yn('Are you an advanced Supybot user?', default=False) - - # Language? - output("""This version of Supybot (known as Limnoria) includes another - language. This can be changed at any time. You need to answer with a short - id for the language, such as 'en', 'fr', 'it' (without the quotes). If - you want to use English, just press enter.""") - language = something('What language do you want to use?', default='en') - - class Empty: - """This is a hack to allow the i18n to get the current language, before - loading the conf module, before the conf module needs i18n to set the - default strings.""" - def __call__(self): - return self.value - fakeConf = Empty() - fakeConf.supybot = Empty() - fakeConf.supybot.language = Empty() - fakeConf.supybot.language.value = language - i18n.conf = fakeConf - i18n.currentLocale = language - i18n.reloadLocales() - import supybot.conf as conf - i18n.import_conf() # It imports the real conf module - - ### Directories. - # We set these variables in cache because otherwise conf and log will - # create directories for the default values, which might not be what the - # user wants. - if advanced: - output("""Now we've got to ask you some questions about where some of - your directories are (or, perhaps, will be :)). If you're running this - wizard from the directory you'll actually be starting your bot from and - don't mind creating some directories in the current directory, then - just don't give answers to these questions and we'll create the - directories we need right here in this directory.""") - - # conf.supybot.directories.log - output("""Your bot will need to put its logs somewhere. Do you have - any specific place you'd like them? If not, just press enter and we'll - make a directory named "logs" right here.""") - (logDir, basedir) = getDirectoryName('logs') - conf.supybot.directories.log.setValue(logDir) - import supybot.log as log - log._stdoutHandler.setLevel(100) # *Nothing* gets through this! - - # conf.supybot.directories.data - output("""Your bot will need to put various data somewhere. Things - like databases, downloaded files, etc. Do you have any specific place - you'd like the bot to put these things? If not, just press enter and - we'll make a directory named "data" right here.""") - (dataDir, basedir) = getDirectoryName('data', basedir=basedir) - conf.supybot.directories.data.setValue(dataDir) - - # conf.supybot.directories.conf - output("""Your bot must know where to find its configuration files. - It'll probably only make one or two, but it's gotta have some place to - put them. Where should that place be? If you don't care, just press - enter and we'll make a directory right here named "conf" where it'll - store its stuff. """) - (confDir, basedir) = getDirectoryName('conf', basedir=basedir) - conf.supybot.directories.conf.setValue(confDir) - - # conf.supybot.directories.backup - output("""Your bot must know where to place backups of its conf and - data files. Where should that place be? If you don't care, just press - enter and we'll make a directory right here named "backup" where it'll - store its stuff.""") - (backupDir, basedir) = getDirectoryName('backup', basedir=basedir) - conf.supybot.directories.backup.setValue(backupDir) - - # conf.supybot.directories.data.tmp - output("""Your bot needs a directory to put temporary files (used - mainly to atomically update its configuration files).""") - (tmpDir, basedir) = getDirectoryName('tmp', basedir=basedir) - conf.supybot.directories.data.tmp.setValue(tmpDir) - - # conf.supybot.directories.data.web - output("""Your bot needs a directory to put files related to the web - server (templates, CSS).""") - (webDir, basedir) = getDirectoryName('web', basedir=basedir) - conf.supybot.directories.data.web.setValue(webDir) - - - # imports callbacks, which imports ircdb, which requires - # directories.conf - import supybot.plugin as plugin - - # pluginDirs - output("""Your bot will also need to know where to find its plugins at. - Of course, it already knows where the plugins that it came with are, - but your own personal plugins that you write for will probably be - somewhere else.""") - pluginDirs = conf.supybot.directories.plugins() - output("""Currently, the bot knows about the following directories:""") - output(format('%L', pluginDirs + [plugin._pluginsDir])) - while yn('Would you like to add another plugin directory? ' - 'Adding a local plugin directory is good style.', - default=True): - (pluginDir, _) = getDirectoryName('plugins', basedir=basedir) - if pluginDir not in pluginDirs: - pluginDirs.append(pluginDir) - conf.supybot.directories.plugins.setValue(pluginDirs) - else: - output("""Your bot needs to create some directories in order to store - the various log, config, and data files.""") - basedir = something("""Where would you like to create these - directories?""", default=os.curdir) - - # conf.supybot.directories.log - (logDir, basedir) = getDirectoryName('logs', - basedir=basedir, prompt=False) - conf.supybot.directories.log.setValue(logDir) - - # conf.supybot.directories.data - (dataDir, basedir) = getDirectoryName('data', - basedir=basedir, prompt=False) - conf.supybot.directories.data.setValue(dataDir) - (tmpDir, basedir) = getDirectoryName('tmp', - basedir=basedir, prompt=False) - conf.supybot.directories.data.tmp.setValue(tmpDir) - (webDir, basedir) = getDirectoryName('web', - basedir=basedir, prompt=False) - conf.supybot.directories.data.web.setValue(webDir) - - # conf.supybot.directories.conf - (confDir, basedir) = getDirectoryName('conf', - basedir=basedir, prompt=False) - conf.supybot.directories.conf.setValue(confDir) - - # conf.supybot.directories.backup - (backupDir, basedir) = getDirectoryName('backup', - basedir=basedir, prompt=False) - conf.supybot.directories.backup.setValue(backupDir) - - # pluginDirs - pluginDirs = conf.supybot.directories.plugins() - (pluginDir, _) = getDirectoryName('plugins', - basedir=basedir, prompt=False) - if pluginDir not in pluginDirs: - pluginDirs.append(pluginDir) - conf.supybot.directories.plugins.setValue(pluginDirs) - - import supybot.log as log - log._stdoutHandler.setLevel(100) # *Nothing* gets through this! - import supybot.plugin as plugin - - output("Good! We're done with the directory stuff.") - - ### - # Bot stuff - ### - output("""Now we're going to ask you things that actually relate to the - bot you'll be running.""") - - network = None - while not network: - output("""First, we need to know the name of the network you'd like to - connect to. Not the server host, mind you, but the name of the - network. If you plan to connect to irc.libera.chat, for instance, - you should answer this question with 'libera' or 'liberachat' - (without the quotes). - """) - network = something('What IRC network will you be connecting to?') - if '.' in network: - output("""There shouldn't be a '.' in the network name. Remember, - this is the network name, not the actual server you plan to connect - to.""") - network = None - elif not registry.isValidRegistryName(network): - output("""That's not a valid name for one reason or another. Please - pick a simpler name, one more likely to be valid.""") - network = None - - conf.supybot.networks.setValue([network]) - network = conf.registerNetwork(network) - - defaultServer = None - server = None - ip = None - while not ip: - serverString = something('What server would you like to connect to?', - default=defaultServer) - if options.network: - try: - output("""Looking up %s...""" % serverString) - ip = socket.gethostbyname(serverString) - except: - output("""Sorry, I couldn't find that server. Perhaps you - misspelled it? Also, be sure not to put the port in the - server's name -- we'll ask you about that later.""") - else: - ip = 'no network available' - - output("""Found %s (%s).""" % (serverString, ip)) - - # conf.supybot.networks..ssl - output("""Most networks allow you to use a secure connection via SSL. - If you are not sure whether this network supports SSL or not, check - its website.""") - use_ssl = yn('Do you want to use an SSL connection?', default=True) - network.ssl.setValue(use_ssl) - - output("""IRC servers almost always accept connections on port - 6697 (or 6667 when not using SSL). They can, however, accept connections - anywhere their admins feel like they want to accept connections from.""") - if yn('Does this server require connection on a non-standard port?', - default=False): - port = 0 - while not port: - port = something('What port is that?') - try: - i = int(port) - if not (0 < i < 65536): - raise ValueError() - except ValueError: - output("""That's not a valid port.""") - port = 0 - else: - if network.ssl.value: - port = 6697 - else: - port = 6667 - server = ':'.join([serverString, str(port)]) - network.servers.setValue([server]) - - # conf.supybot.nick - # Force the user into specifying a nick if it didn't have one already - while True: - nick = something('What nick would you like your bot to use?', - default=None) - try: - conf.supybot.nick.set(nick) - break - except registry.InvalidRegistryValue: - output("""That's not a valid nick. Go ahead and pick another.""") - - # conf.supybot.networks..sasl.{username,password} - output("""Most networks allow you to create an account, using services - typically known as NickServ. If you already have one, you can configure - the bot to use that account by proving your username and password - (also known as "SASL plain"). - It is usually not a requirement.""") - if yn('Do you want your bot to use a network account?', default=False): - while True: - username = something('What account name should the bot use?') - password = getpass('What is the password of that account?') - if not username: - if yn('Missing username. Do you want to try again?'): - continue - if not password: - if yn('Missing password. Do you want to try again?'): - continue - network.sasl.username.set(username) - network.sasl.password.set(password) - break - - # conf.supybot.user - if advanced: - output("""If you've ever done a /whois on a person, you know that IRC - provides a way for users to show the world their full name. What would - you like your bot's full name to be? If you don't care, just press - enter and it'll be the same as your bot's nick.""") - user = '' - user = something('What would you like your bot\'s full name to be?', - default=nick) - conf.supybot.user.set(user) - # conf.supybot.ident (if advanced) - defaultIdent = 'limnoria' - if advanced: - output("""IRC servers also allow you to set your ident, which they - might need if they can't find your identd server. What would you like - your ident to be? If you don't care, press enter and we'll use - 'limnoria'. In fact, we prefer that you do this, because it provides - free advertising for Supybot when users /whois your bot. But, of - course, it's your call.""") - while True: - ident = something('What would you like your bot\'s ident to be?', - default=defaultIdent) - try: - conf.supybot.ident.set(ident) - break - except registry.InvalidRegistryValue: - output("""That was not a valid ident. Go ahead and pick - another.""") - else: - conf.supybot.ident.set(defaultIdent) - - # conf.supybot.networks..password - output("""Some servers require a password to connect to them. Most - public servers don't. If you try to connect to a server and for some - reason it just won't work, it might be that you need to set a - password.""") - if yn('Do you want to set such a password?', default=False): - network.password.set(getpass()) - - # conf.supybot.networks..channels - output("""Of course, having an IRC bot isn't the most useful thing in the - world unless you can make that bot join some channels.""") - if yn('Do you want your bot to join some channels when it connects?', - default=True): - defaultChannels = ' '.join(network.channels()) - output("""Separate channels with spaces. If the channel is locked - with a key, follow the channel name with the key separated - by a comma. For example: - #supybot-bots #mychannel,mykey #otherchannel"""); - while True: - channels_input = something('What channels?', default=defaultChannels) - channels = [] - error_ = False - for channel in channels_input.split(): - L = channel.split(',') - if len(L) == 0: - continue - elif len(L) == 1: - (channel,) = L - channels.append(channel) - elif len(L) == 2: - (channel, key) = L - channels.append(channel) - network.channels.key.get(channel).setValue(key) - else: - output("""Too many commas in %s.""" % channel) - error_ = True - break - if error_: - break - try: - network.channels.set(' '.join(channels)) - break - except registry.InvalidRegistryValue as e: - output(""""%s" is an invalid IRC channel. Be sure to prefix - the channel with # (or +, or !, or &, but no one uses those - channels, really). Be sure the channel key (if you are - supplying one) does not contain a comma.""" % e.channel) - else: - network.channels.setValue([]) - - ### - # Plugins - ### - def configurePlugin(module, advanced): - if hasattr(module, 'configure'): - output("""Beginning configuration for %s...""" % - module.Class.__name__) - module.configure(advanced) - print() # Blank line :) - output("""Done!""") - else: - conf.registerPlugin(module.__name__, currentValue=True) - - plugins = getPlugins(pluginDirs + [plugin._pluginsDir]) - for s in ('Admin', 'User', 'Channel', 'Misc', 'Config', 'Utilities'): - m = loadPlugin(s) - if m is not None: - configurePlugin(m, advanced) - else: - error('There was an error loading one of the core plugins that ' - 'under almost all circumstances are loaded. Go ahead and ' - 'fix that error and run this script again.') - clearLoadedPlugins(plugins, conf.supybot.plugins) - - output("""Now we're going to run you through plugin configuration. There's - a variety of plugins in supybot by default, but you can create and - add your own, of course. We'll allow you to take a look at the known - plugins' descriptions and configure them - if you like what you see.""") - - # bulk - addedBulk = False - if advanced and yn('Would you like to add plugins en masse first?'): - addedBulk = True - output(format("""The available plugins are: %L.""", plugins)) - output("""What plugins would you like to add? If you've changed your - mind and would rather not add plugins in bulk like this, just press - enter and we'll move on to the individual plugin configuration. - We suggest you to add Aka, Ctcp, Later, Network, Plugin, String, - and Utilities""") - massPlugins = anything('Separate plugin names by spaces or commas:') - for name in re.split(r',?\s+', massPlugins): - module = loadPlugin(name) - if module is not None: - configurePlugin(module, advanced) - clearLoadedPlugins(plugins, conf.supybot.plugins) - - # individual - if yn('Would you like to look at plugins individually?'): - output("""Next comes your opportunity to learn more about the plugins - that are available and select some (or all!) of them to run in your - bot. Before you have to make a decision, of course, you'll be able to - see a short description of the plugin and, if you choose, an example - session with the plugin. Let's begin.""") - # until we get example strings again, this will default to false - #showUsage =yn('Would you like the option of seeing usage examples?') - showUsage = False - name = expect('What plugin would you like to look at?', - plugins, acceptEmpty=True) - while name: - module = loadPlugin(name) - if module is not None: - describePlugin(module, showUsage) - if yn('Would you like to load this plugin?', default=True): - configurePlugin(module, advanced) - clearLoadedPlugins(plugins, conf.supybot.plugins) - if not yn('Would you like add another plugin?'): - break - name = expect('What plugin would you like to look at?', plugins) - - ### - # Sundry - ### - output("""Although supybot offers a supybot-adduser script, with which - you can add users to your bot's user database, it's *very* important that - you have an owner user for you bot.""") - if yn('Would you like to add an owner user for your bot?', default=True): - import supybot.ircdb as ircdb - name = something('What should the owner\'s username be?') - try: - id = ircdb.users.getUserId(name) - u = ircdb.users.getUser(id) - if u._checkCapability('owner'): - output("""That user already exists, and has owner capabilities - already. Perhaps you added it before? """) - if yn('Do you want to remove its owner capability?', - default=False): - u.removeCapability('owner') - ircdb.users.setUser(u) - else: - output("""That user already exists, but doesn't have owner - capabilities.""") - if yn('Do you want to add to it owner capabilities?', - default=False): - u.addCapability('owner') - ircdb.users.setUser(u) - except KeyError: - password = getpass('What should the owner\'s password be?') - u = ircdb.users.newUser() - u.name = name - u.setPassword(password) - u.addCapability('owner') - ircdb.users.setUser(u) - - output("""Of course, when you're in an IRC channel you can address the bot - by its nick and it will respond, if you give it a valid command (it may or - may not respond, depending on what your config variable replyWhenNotCommand - is set to). But your bot can also respond to a short "prefix character," - so instead of saying "bot: do this," you can say, "@do this" and achieve - the same effect. Of course, you don't *have* to have a prefix char, but - if the bot ends up participating significantly in your channel, it'll ease - things.""") - if yn('Would you like to set the prefix char(s) for your bot? ', - default=True): - output("""Enter any characters you want here, but be careful: they - should be rare enough that people don't accidentally address the bot - (simply because they'll probably be annoyed if they do address the bot - on accident). You can even have more than one. I (jemfinch) am quite - partial to @, but that's because I've been using it since my ocamlbot - days.""") - import supybot.callbacks as callbacks - c = '' - while not c: - try: - c = anything('What would you like your bot\'s prefix ' - 'character(s) to be?') - conf.supybot.reply.whenAddressedBy.chars.set(c) - except registry.InvalidRegistryValue as e: - output(str(e)) - c = '' - else: - conf.supybot.reply.whenAddressedBy.chars.set('') - - ### - # logging variables. - ### - - if advanced: - # conf.supybot.log.stdout - output("""By default, your bot will log not only to files in the logs - directory you gave it, but also to stdout. We find this useful for - debugging, and also just for the pretty output (it's colored!)""") - stdout = not yn('Would you like to turn off this logging to stdout?', - default=False) - conf.supybot.log.stdout.setValue(stdout) - if conf.supybot.log.stdout(): - # conf.something - output("""Some terminals may not be able to display the pretty - colors logged to stderr. By default, though, we turn the colors - off for Windows machines and leave it on for *nix machines.""") - if os.name != 'nt': - conf.supybot.log.stdout.colorized.setValue( - not yn('Would you like to turn this colorization off?', - default=False)) - - # conf.supybot.log.level - output("""Your bot can handle debug messages at several priorities, - CRITICAL, ERROR, WARNING, INFO, and DEBUG, in decreasing order of - priority. By default, your bot will log all of these priorities except - DEBUG. You can, however, specify that it only log messages above a - certain priority level.""") - priority = str(conf.supybot.log.level) - logLevel = something('What would you like the minimum priority to be?' - ' Just press enter to accept the default.', - default=priority).lower() - while logLevel not in ['debug','info','warning','error','critical']: - output("""That's not a valid priority. Valid priorities include - 'DEBUG', 'INFO', 'WARNING', 'ERROR', and 'CRITICAL'""") - logLevel = something('What would you like the minimum priority to ' - 'be? Just press enter to accept the default.', - default=priority).lower() - conf.supybot.log.level.set(logLevel) - - # conf.supybot.databases.plugins.channelSpecific - - output("""Many plugins in Supybot are channel-specific. Their - databases, likewise, are specific to each channel the bot is in. Many - people don't want this, so we have one central location in which to - say that you would prefer all databases for all channels to be shared. - This variable, supybot.databases.plugins.channelSpecific, is that - place.""") - - conf.supybot.databases.plugins.channelSpecific.setValue( - not yn('Would you like plugin databases to be shared by all ' - 'channels, rather than specific to each channel the ' - 'bot is in?')) - - output("""There are a lot of options we didn't ask you about simply - because we'd rather you get up and running and have time - left to play around with your bot. But come back and see - us! When you've played around with your bot enough to - know what you like, what you don't like, what you'd like - to change, then take a look at your configuration file - when your bot isn't running and read the comments, - tweaking values to your heart's desire.""") - - # Let's make sure that src/ plugins are loaded. - conf.registerPlugin('Admin', True) - conf.registerPlugin('AutoMode', True) - conf.registerPlugin('Channel', True) - conf.registerPlugin('Config', True) - conf.registerPlugin('Misc', True) - conf.registerPlugin('Network', True) - conf.registerPlugin('NickAuth', True) - conf.registerPlugin('User', True) - conf.registerPlugin('Utilities', True) - - ### - # Write the registry - ### - - # We're going to need to do a darcs predist thing here. - #conf.supybot.debug.generated.setValue('...') - - if advanced: - basedir = '.' - filename = os.path.join(basedir, '%s.conf') - - filename = something("""In which file would you like to save - this config?""", default=filename % nick) - if not filename.endswith('.conf'): - filename += '.conf' - registry.close(conf.supybot, os.path.expanduser(filename)) - - # Done! - output("""All done! Your new bot configuration is %s. If you're running - a *nix based OS, you can probably start your bot with the command line - "supybot %s". If you're not running a *nix or similar machine, you'll - just have to start it like you start all your other Python scripts.""" % \ - (filename, filename)) - -if __name__ == '__main__': - try: - main() - except KeyboardInterrupt: - # We may still be using bold text when exiting during a prompt - if questions.useBold: - import supybot.ansi as ansi - print(ansi.RESET) - print() - print() - output("""Well, it looks like you canceled out of the wizard before - it was done. Unfortunately, I didn't get to write anything to file. - Please run the wizard again to completion.""") - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/setup.py limnoria-2023.5.27/setup.py --- limnoria-2023.1.28/setup.py 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/setup.py 2023-05-27 06:02:01.000000000 +0000 @@ -117,7 +117,9 @@ 'supybot.locales', 'supybot.utils', 'supybot.drivers', - 'supybot.plugins',] + \ + 'supybot.plugins', + 'supybot.scripts', + ] + \ ['supybot.plugins.'+s for s in plugins] + \ [ 'supybot.plugins.Dict.local', @@ -152,6 +154,17 @@ if files: package_data.update({plugin_name: files}) +scripts = [ + '', + '-test', + '-botchk', + '-wizard', + '-adduser', + '-reset-password', + '-plugin-doc', + '-plugin-create', +] + setup( # Metadata name='limnoria', @@ -191,6 +204,8 @@ 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Communications :: Chat :: Internet Relay Chat', 'Topic :: Software Development :: Libraries :: Python Modules', ], @@ -202,25 +217,22 @@ package_data=package_data, - scripts=['scripts/supybot', - 'scripts/supybot-test', - 'scripts/supybot-botchk', - 'scripts/supybot-wizard', - 'scripts/supybot-adduser', - 'scripts/supybot-reset-password', - 'scripts/supybot-plugin-doc', - 'scripts/supybot-plugin-create', - ], - data_files=[('share/man/man1', ['man/supybot.1']), - ('share/man/man1', ['man/supybot-test.1']), - ('share/man/man1', ['man/supybot-botchk.1']), - ('share/man/man1', ['man/supybot-wizard.1']), - ('share/man/man1', ['man/supybot-adduser.1']), - ('share/man/man1', ['man/supybot-reset-password.1']), - ('share/man/man1', ['man/supybot-plugin-doc.1']), - ('share/man/man1', ['man/supybot-plugin-create.1']), - ], - - ) + entry_points={ + 'console_scripts': [ + '%s%s = supybot.scripts.limnoria%s:main' + % (prefix, name, name.replace('-', '_')) + for name in scripts + for prefix in ('supybot', 'limnoria') + ] + }, + + data_files=[ + ('share/man/man1', [ + 'man/%s%s.1' % (prefix, name) + for name in scripts + for prefix in ('supybot', 'limnoria') + ]), + ], +) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/drivers/Socket.py limnoria-2023.5.27/src/drivers/Socket.py --- limnoria-2023.1.28/src/drivers/Socket.py 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/src/drivers/Socket.py 2023-05-18 13:06:27.000000000 +0000 @@ -70,8 +70,6 @@ self._attempt = -1 self.servers = () self.eagains = 0 - self.inbuffer = b'' - self.outbuffer = '' self.zombie = False self.connected = False self.writeCheckTime = None @@ -248,6 +246,8 @@ def reconnect(self, wait=False, reset=True, server=None): self._attempt += 1 + self.inbuffer = b'' + self.outbuffer = '' self.nextReconnectTime = None if self.connected: self.onDisconnect() diff -Nru limnoria-2023.1.28/src/irclib.py limnoria-2023.5.27/src/irclib.py --- limnoria-2023.1.28/src/irclib.py 2023-02-11 09:14:15.000000000 +0000 +++ limnoria-2023.5.27/src/irclib.py 2023-05-18 13:06:27.000000000 +0000 @@ -1776,7 +1776,8 @@ 'multi-prefix', 'metadata-notify', 'account-tag', 'userhost-in-names', 'invite-notify', 'server-time', 'chghost', 'batch', 'away-notify', 'message-tags', - 'msgid', 'setname', 'labeled-response', 'echo-message']) + 'msgid', 'setname', 'labeled-response', 'echo-message', + 'standard-replies']) """IRCv3 capabilities requested when they are available. echo-message is special-cased to be requested only with labeled-response. diff -Nru limnoria-2023.1.28/src/scripts/limnoria_adduser.py limnoria-2023.5.27/src/scripts/limnoria_adduser.py --- limnoria-2023.1.28/src/scripts/limnoria_adduser.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_adduser.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2002-2004, Jeremiah Fincher +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + + + +import supybot + +from supybot.questions import * + +import os +import sys +import optparse + +def main(): + import supybot.log as log + import supybot.conf as conf + conf.supybot.log.stdout.setValue(False) + parser = optparse.OptionParser(usage='Usage: %prog [options] ', + version='supybot %s' % conf.version) + parser.add_option('-u', '--username', action='store', default='', + dest='name', + help='username for the user.') + parser.add_option('-p', '--password', action='store', default='', + dest='password', + help='password for the user.') + parser.add_option('-c', '--capability', action='append', + dest='capabilities', metavar='CAPABILITY', + help='capability the user should have; ' + 'this option may be given multiple times.') + (options, args) = parser.parse_args() + if len(args) != 1: + parser.error('Specify the users.conf file you\'d like to use. ' + 'Be sure *not* to specify your registry file, generated ' + 'by supybot-wizard. This is not the file you want. ' + 'Instead, take a look in your conf directory (usually ' + 'named "conf") and take a gander at the file ' + '"users.conf". That\'s the one you want.') + + filename = os.path.abspath(args[0]) + conf.supybot.directories.log.setValue('/') + conf.supybot.directories.conf.setValue('/') + conf.supybot.directories.data.setValue('/') + conf.supybot.directories.plugins.setValue(['/']) + conf.supybot.databases.users.filename.setValue(filename) + import supybot.ircdb as ircdb + + if not options.name: + name = '' + while not name: + name = something('What is the user\'s name?') + try: + # Check to see if the user is already in the database. + _ = ircdb.users.getUser(name) + # Uh oh. That user already exists; + # otherwise we'd have KeyError'ed. + output('That user already exists. Try another name.') + name = '' + except KeyError: + # Good. No such user exists. We'll pass. + pass + else: + try: + # Same as above. We exit here instead. + _ = ircdb.users.getUser(options.name) + output('That user already exists. Try another name.') + sys.exit(-1) + except KeyError: + name = options.name + + if not options.password: + password = getpass('What is %s\'s password? ' % name) + else: + password = options.password + + if not options.capabilities: + capabilities = [] + prompt = 'Would you like to give %s a capability?' % name + while yn(prompt): + capabilities.append(anything('What capability?')) + prompt = 'Would you like to give %s another capability?' % name + else: + capabilities = options.capabilities + + user = ircdb.users.newUser() + user.name = name + user.setPassword(password) + for capability in capabilities: + user.addCapability(capability) + ircdb.users.setUser(user) + ircdb.users.flush() + #os.system('cat %s' % filename) # Was this here just for debugging? + ircdb.users.close() + print('User %s added.' % name) + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + pass + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/scripts/limnoria_botchk.py limnoria-2023.5.27/src/scripts/limnoria_botchk.py --- limnoria-2023.1.28/src/scripts/limnoria_botchk.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_botchk.py 2023-05-27 06:02:01.000000000 +0000 @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2005, Jeremiah Fincher +# Copyright (c) 2009, James McCoy +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +import os +import sys +import optparse +import subprocess + +# XXX I wanted this for conf.version, but this will create directories. We +# really need to refactor conf so it either doesn't create directories, or +# so that static information (like the version) can be imported from +# somewhere else. +# from supybot import conf + +VERBOSE = False + +def readPid(filename): + fd = open(filename) + try: + return int(fd.read().strip()) + finally: + fd.close() + +def isAlive(pid): + try: + os.kill(pid, 0) + return True + except OSError: + return False + +def debug(s): + if VERBOSE: + if not s.endswith(os.linesep): + s += os.linesep + sys.stdout.write(s) + +def main(): + parser = optparse.OptionParser(usage='Usage: %prog [options]') + parser.add_option('', '--verbose', action='store_true', + help='Makes output verbose.') + parser.add_option('', '--botdir', + help='Determines what directory the bot resides in and ' + 'should be started from.') + parser.add_option('', '--pidfile', + help='Determines what file to look in for the pid of ' + 'the running bot. This should be relative to the ' + 'given bot directory. Note that for this to actually ' + 'work, you have to make a matching entry in the ' + 'supybot.pidFile config in the supybot registry.') + parser.add_option('', '--supybot', default='supybot', + help='Determines where the supybot executable is ' + 'located. If not given, assumes that supybot is ' + 'in $PATH.') + parser.add_option('', '--conffile', + help='Determines what configuration file should be ' + 'given to the supybot executable when (re)starting the ' + 'bot.') + + (options, args) = parser.parse_args() + VERBOSE = options.verbose + + if args: + parser.error('Extra arguments given.') + if not options.botdir: + parser.error('No botdir given.') + if not options.pidfile: + parser.error('No pidfile given.') + if not options.conffile: + parser.error('No conffile given.') + + os.chdir(options.botdir) + open(options.pidfile, 'a').close() + + pid = None + try: + pid = readPid(options.pidfile) + debug('Found pidFile with proper pid contents of %s' % pid) + except ValueError as e: + foundBot = False + + if pid is not None: + foundBot = isAlive(pid) + if foundBot: + debug('Pid %s is alive and belongs to us.' % pid) + else: + debug('Pid %s is not the bot.' % pid) + + if not foundBot: + # First, we check if the pidfile is writable. If not, supybot will just exit, + # so we go ahead and refuse to start it. + try: + open(options.pidfile, 'r+') + except EnvironmentError as e: + debug('pidfile (%s) is not writable: %s' % (options.pidfile, e)) + sys.exit(-1) + debug('Bot not found, starting.') + cmdline = [options.supybot, '--daemon', options.conffile] + inst = subprocess.Popen(cmdline, close_fds=True, + stderr=subprocess.STDOUT, + stdin=None, stdout=subprocess.PIPE) + debug('Output from supybot: %r' % inst.stdout.read()) + ret = inst.wait() + debug('Bot started, command line %r returned %s.' % (' '.join(cmdline), + ret)) + sys.exit(ret) + else: + sys.exit(0) + +if __name__ == '__main__': + main() + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/scripts/limnoria_plugin_create.py limnoria-2023.5.27/src/scripts/limnoria_plugin_create.py --- limnoria-2023.1.28/src/scripts/limnoria_plugin_create.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_plugin_create.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2002-2005, Jeremiah Fincher +# Copyright (c) 2009, James McCoy +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +from __future__ import print_function + +import supybot + +import os +import sys +import time +import os.path +import optparse + +def error(s): + sys.stderr.write(textwrap.fill(s)) + sys.stderr.write(os.linesep) + sys.exit(-1) + +import supybot.conf as conf +from supybot.questions import * + +copyright = ''' +### +# Copyright (c) %s, %%s +# All rights reserved. +# +%%s +### +''' % time.strftime('%Y') +# Here we use strip() instead of lstrip() on purpose. +copyright = copyright.strip() + +license = ''' +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +''' +license = license.lstrip() + +pluginTemplate = ''' +%s + +from supybot import utils, plugins, ircutils, callbacks +from supybot.commands import * +from supybot.i18n import PluginInternationalization + + +_ = PluginInternationalization('%s') + + +class %s(callbacks.Plugin): + """%s""" + %s + + +Class = %s + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: +'''.lstrip() # This removes the newlines that precede and follow the text. + +configTemplate = ''' +%s + +from supybot import conf, registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('%s') +except: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified themself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin(%r, True) + + +%s = conf.registerPlugin(%r) +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(%s, 'someConfigVariableName', +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: +'''.lstrip() + + +__init__Template = ''' +%s + +""" +%s: %s +""" + +import sys +import supybot +from supybot import world + +# Use this for the version of this plugin. +__version__ = "" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.authors.unknown + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +# This is a url where the most recent plugin package can be downloaded. +__url__ = '' + +from . import config +from . import plugin +from importlib import reload +# In case we're being reloaded. +reload(config) +reload(plugin) +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + from . import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: +'''.lstrip() + +setupTemplate = ''' +%s + +from supybot.setup import plugin_setup + +plugin_setup( + %r, +) +'''.lstrip() + +testTemplate = ''' +%s + +from supybot.test import * + + +class %sTestCase(PluginTestCase): + plugins = (%r,) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: +'''.lstrip() + +readmeTemplate = ''' +%s +'''.lstrip() + +def _main(): + global copyright + global license + parser = optparse.OptionParser(usage='Usage: %prog [options]', + version='Supybot %s' % conf.version) + parser.add_option('-n', '--name', action='store', dest='name', + help='sets the name for the plugin.') + parser.add_option('-t', '--thread', action='store_true', dest='threaded', + help='makes the plugin threaded.') + parser.add_option('-a', '--author', '--real-name', action='store', + dest='realName', help='Determines who the copyright is ' + 'assigned to.') + parser.add_option('-d', '--desc', action='store', dest='desc', + help='Short description of plugin.') + (options, args) = parser.parse_args() + if options.name: + name = options.name + threaded = options.threaded + else: + name = something('What should the name of the plugin be?') + if name.endswith('.py'): + name = name[:-3] + while name[0].islower(): + print('Plugin names must begin with a capital letter.') + name = something('What should the name of the plugin be?') + if name.endswith('.py'): + name = name[:-3] + + if os.path.exists(name): + error('A file or directory named %s already exists; remove or ' + 'rename it and run this program again.' % name) + + print(textwrap.fill(textwrap.dedent(""" + Sometimes you'll want a callback to be threaded. If its methods + (command or regexp-based, either one) will take a significant amount + of time to run, you'll want to thread them so they don't block the + entire bot.""").strip())) + print() + threaded = yn('Does your plugin need to be threaded?') + + if options.realName: + realName = options.realName + else: + realName = something(textwrap.dedent(""" + What is your name, so I can fill in the copyright and license + appropriately? + """).strip()) + + if not yn('Do you wish to use Supybot\'s license for your plugin?'): + license = '#' + + if not options.desc: + options.desc = something(textwrap.dedent(""" + Please provide a short description of the plugin: + """).strip()) + + if threaded: + threaded = 'threaded = True' + else: + threaded = 'pass' + if name.endswith('.py'): + name = name[:-3] + while name[0].islower(): + print('Plugin names must begin with a capital letter.') + name = something('What should the name of the plugin be?') + if name.endswith('.py'): + name = name[:-3] + copyright %= (realName, license) + pathname = name + + # Make the directory. + os.mkdir(pathname) + + def writeFile(filename, s): + fd = open(os.path.join(pathname, filename), 'w') + try: + fd.write(s) + finally: + fd.close() + + writeFile('plugin.py', pluginTemplate % (copyright, name, name, + options.desc, threaded, name)) + writeFile('config.py', configTemplate % (copyright, name, name, name, name, + name)) + writeFile('__init__.py', __init__Template % (copyright, name, options.desc)) + writeFile('setup.py', setupTemplate % (copyright, name)) + writeFile('test.py', testTemplate % (copyright, name, name)) + writeFile('README.md', readmeTemplate % (options.desc,)) + + pathname = os.path.join(pathname, 'local') + os.mkdir(pathname) + writeFile('__init__.py', + '# Stub so local is a module, used for third-party modules\n') + + print('Your new plugin template is in the %s directory.' % name) + +def main(): + try: + _main() + except KeyboardInterrupt: + print() + output("""It looks like you cancelled out of this script before it was + finished. Obviously, nothing was written, but just run this script + again whenever you want to generate a template for a plugin.""") + +if __name__ == '__main__': + main() + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/scripts/limnoria_plugin_doc.py limnoria-2023.5.27/src/scripts/limnoria_plugin_doc.py --- limnoria-2023.1.28/src/scripts/limnoria_plugin_doc.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_plugin_doc.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,356 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2005, Ali Afshar +# Copyright (c) 2009, James McCoy +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +import os +import sys +import shutil +import string +import logging + +import supybot + +def error(s): + sys.stderr.write('%s\n' % s) + sys.exit(-1) + +# We need to do this before we import conf. +if not os.path.exists('doc-conf'): + os.mkdir('doc-conf') + +registryFilename = os.path.join('doc-conf', 'doc.conf') +try: + fd = open(registryFilename, 'w') + fd.write(""" +supybot.directories.data: doc-data +supybot.directories.conf: doc-conf +supybot.directories.log: doc-logs +supybot.log.stdout: False +supybot.log.level: DEBUG +supybot.log.format: %(levelname)s %(message)s +supybot.log.plugins.individualLogfiles: False +supybot.databases: sqlite3 anydbm cdb flat pickle +""") + fd.close() +except EnvironmentError as e: + error('Unable to open %s for writing.' % registryFilename) + +import supybot.registry as registry +registry.open_registry(registryFilename) + +import supybot.log as log +import supybot.conf as conf +conf.supybot.flush.setValue(False) + +import textwrap + +import supybot.utils as utils +import supybot.world as world +import supybot.plugin as plugin +import supybot.registry as registry + +world.documenting = True + +TITLE_TEMPLATE = 'Documentation for the $name plugin for Supybot' + +class PluginDoc(object): + def __init__(self, mod, titleTemplate): + self.mod = mod + self.inst = self.mod.Class(None) + self.name = self.inst.name() + self.appendExtraBlankLine = False + self.titleTemplate = string.Template(titleTemplate) + self.lines = [] + + def appendLine(self, line, indent=0): + line = line.strip() + indent = ' ' * indent + # this looked like a good idea, but it's actually breaking lines + # in the middle of ReST elements: + #lines = textwrap.wrap(line, 79, + # initial_indent=indent, + # subsequent_indent=indent) + lines = [indent + line.replace('\n', '\n' + indent)] + self.lines.extend(lines) + if self.appendExtraBlankLine: + self.lines.append('') + + def renderRST(self): + self.appendExtraBlankLine = False + self.appendLine('.. _plugin-%s:' % self.name) + self.lines.append('') + s = self.titleTemplate.substitute(name=self.name) + self.appendLine(s) + self.appendLine('=' * len(s)) + self.lines.append('') + self.appendLine('Purpose') + self.appendLine('-------') + self.lines.append('') + pdoc = getattr(self.mod, '__doc__', + 'My author didn\'t give me a purpose.') + self.appendLine(pdoc) + self.lines.append('') + cdoc = getattr(self.mod.Class, '__doc__', None) + if cdoc is not None: + self.appendLine('Usage') + self.appendLine('-----') + self.lines.append('') + # We add spaces at the beginning in case the docstring does not + # start with a newline (the default, unfortunately) + self.appendLine(textwrap.dedent(" "* 1000 + cdoc).lstrip()) + self.lines.append('') + commands = self.inst.listCommands() + if len(commands): + self.lines.append('.. _commands-%s:\n' % self.name) + self.appendLine('Commands') + self.appendLine('--------') + self.lines.append('') + for command in commands: + log.debug('command: %s', command) + self.lines.append('.. _command-%s-%s:\n' % + (self.name.lower(), command.replace(' ', '.'))) + line = '%s ' % command + command = command.split() + doc = self.inst.getCommandHelp(command) + if doc: + doc = doc.replace('\x02', '') + (args, help) = doc.split(')', 1) + args = args.split('(', 1)[1] + args = args[len(' '.join(command)):].strip() + help = help.split('--', 1)[1].strip() + self.appendLine(line + args) + self.appendLine(help, 1) + else: + self.appendLine('No help associated with this command') + self.lines.append('') + # now the config + try: + confs = conf.supybot.plugins.get(self.name) + self.lines.append('.. _conf-%s:\n' % self.name) + self.appendLine('Configuration') + self.appendLine('-------------') + self.lines.append('') + except registry.NonExistentRegistryEntry: + log.info('No configuration for plugin %s', plugin) + self.appendLine('No configuration for this plugin') + else: + for confValues in self.genConfig(confs, 0): + (name, kind, help, default, indent) = confValues + self.appendLine('.. _conf-%s:' % name, indent - 1) + self.lines.append('\n') + self.appendLine('%s' % name, indent - 1) + if default: + self.appendLine( + ('This config variable defaults to %s, %s') + % (default, kind), indent) + else: + self.appendLine('This %s' % kind, indent) + if help: + self.lines.append('') + self.appendLine(help, indent) + self.lines.append('') + return '\n'.join(self.lines) + '\n' + + def renderSTX(self): + self.appendExtraBlankLine = True + self.appendLine(self.titleTemplate.substitute(name=self.name)) + self.appendLine('Purpose', 1) + pdoc = getattr(self.mod, '__doc__', + 'My author didn\'t give me a purpose.') + self.appendLine(pdoc, 2) + cdoc = getattr(self.mod.Class, '__doc__', None) + if cdoc is not None: + self.appendLine('Usage', 1) + self.appendLine(cdoc, 2) + commands = self.inst.listCommands() + if len(commands): + self.appendLine('Commands', 1) + for command in commands: + log.debug('command: %s', command) + line = '* %s ' % command + command = command.split() + doc = self.inst.getCommandHelp(command) + if doc: + doc = doc.replace('\x02', '') + (args, help) = doc.split(')', 1) + args = args.split('(', 1)[1] + args = args[len(' '.join(command)):].strip() + help = help.split('--', 1)[1].strip() + self.appendLine(line + args, 2) + self.appendLine(help, 3) + else: + self.appendLine('No help associated with this command', 3) + # now the config + try: + confs = conf.supybot.plugins.get(self.name) + self.appendLine('Configuration', 1) + except registry.NonExistentRegistryEntry: + log.info('No configuration for plugin %s', plugin) + self.appendLine('No configuration for this plugin', 2) + else: + for confValues in self.genConfig(confs, 2): + (name, kind, help, default, indent) = confValues + self.appendLine('* %s' % name, indent - 1) + if default: + self.appendLine( + ('This config variable defaults to %s, %s') + % (default, kind), indent) + else: + self.appendLine('This %s' % kind, indent) + self.appendLine(help, indent) + return '\n'.join(self.lines) + '\n' + + def genConfig(self, item, origindent): + confVars = item.getValues(getChildren=False, fullNames=False) + if not confVars: + return + for (c, v) in confVars: + name = v._name + indent = origindent + 1 + if isinstance(v, registry.Value): + try: + default = utils.str.dqrepr(str(v)) + help = v.help() + except registry.NonExistentRegistryEntry: + pass + else: + cv = '' if v._channelValue else ' not' + nv = '' if v._networkValue else ' not' + kind = ( + 'is%s network-specific, and is%s channel-specific.' + % (nv, cv) + ) + else: + help = '' + default = '' + kind = 'is a group of:' + yield (name, kind, help, default, indent) + for confValues in self.genConfig(v, indent): + yield confValues + +def genDoc(m, options): + Plugin = PluginDoc(m, options.titleTemplate) + print('Generating documentation for %s...' % Plugin.name) + outputFilename = string.Template(options.outputFilename).substitute( + name=Plugin.name, format=options.format) + path = os.path.join(options.outputDir, outputFilename) + try: + fd = open(path, 'w') + except EnvironmentError as e: + error('Unable to open %s for writing.' % path) + f = getattr(Plugin, 'render%s' % options.format.upper(), None) + if f is None: + fd.close() + error('Unknown render format: `%s\'' % options.format) + try: + fd.write(f()) + finally: + fd.close() + +def main(): + import glob + import os.path + import optparse + import supybot.plugin as plugin + + parser = optparse.OptionParser(usage='Usage: %prog [options] [plugins]', + version='Supybot %s' % conf.version) + parser.add_option('-c', '--clean', action='store_true', default=False, + dest='clean', help='Cleans the various data/conf/logs ' + 'directories after generating the docs.') + parser.add_option('-o', '--output-dir', dest='outputDir', default='.', + help='Specifies the directory in which to write the ' + 'documentation for the plugin.') + parser.add_option('--output-filename', dest='outputFilename', + default='$name.$format', + help='Specifies the path of individual output files. ' + 'If present in the value, "$name" will be substituted ' + 'with the plugin\'s name and "$format" with the value ' + 'if --format.') + parser.add_option('-f', '--format', dest='format', choices=['rst', 'stx'], + default='rst', help='Specifies which output format to ' + 'use.') + parser.add_option('--plugins-dir', + action='append', dest='pluginsDirs', default=[], + help='Looks in in the given directory for plugins and ' + 'generates documentation for all of them.') + parser.add_option('--title-template', + default=TITLE_TEMPLATE, dest='titleTemplate', + help='Template string for the title of generated files') + (options, args) = parser.parse_args() + + # This must go before checking for args, of course. + for pluginDir in options.pluginsDirs: + for name in glob.glob(os.path.join(pluginDir, '*')): + if os.path.isdir(name): + args.append(name) + + if not args: + parser.print_help() + sys.exit(-1) + + args = [s.rstrip('\\/') for s in args] + pluginDirs = set([os.path.dirname(s) or '.' for s in args]) + conf.supybot.directories.plugins.setValue(list(pluginDirs)) + pluginNames = set([os.path.basename(s) for s in args]) + plugins = set([]) + for pluginName in pluginNames: + if pluginName == '__pycache__': + continue + if pluginName.endswith('.py'): + pluginName = pluginName[:-3] + try: + pluginModule = plugin.loadPluginModule(pluginName) + except ImportError as e: + s = 'Failed to load plugin %s: %s\n' \ + '(pluginDirs: %s)' % (pluginName, e, + conf.supybot.directories.plugins()) + error(s) + plugins.add(pluginModule) + + for Plugin in plugins: + genDoc(Plugin, options) + + if options.clean: + # We are about to remove the log dir; so trying to write anything + # (such as "Shutdown initiated." and friends, from atexit callbacks) + # would result in errors. + log._handler.setLevel(logging.CRITICAL) + shutil.rmtree(conf.supybot.directories.log()) + shutil.rmtree(conf.supybot.directories.conf()) + shutil.rmtree(conf.supybot.directories.data()) + + +if __name__ == '__main__': + main() + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=78: diff -Nru limnoria-2023.1.28/src/scripts/limnoria.py limnoria-2023.5.27/src/scripts/limnoria.py --- limnoria-2023.1.28/src/scripts/limnoria.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,371 @@ +### +# Copyright (c) 2003-2004, Jeremiah Fincher +# Copyright (c) 2009, James McCoy +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +""" +This is the main program to run Supybot. +""" + +import re +import os +import sys +import atexit +import shutil +import signal + +from io import StringIO # Import this after version check since this will fail on Python 2 + +def _termHandler(signalNumber, stackFrame): + raise SystemExit('Signal #%s.' % signalNumber) + +signal.signal(signal.SIGTERM, _termHandler) + +import time +import optparse +import textwrap + +started = time.time() + +import supybot +import supybot.utils as utils +import supybot.registry as registry +import supybot.questions as questions +import supybot.ircutils as ircutils +try: + import supybot.i18n as i18n +except ImportError: + sys.stderr.write("""Error: + You are running a mix of Limnoria and stock Supybot code. Although you run + one of Limnoria\'s executables, Python tries to load stock + Supybot\'s library. To fix this issue, uninstall Supybot + ("%s -m pip uninstall supybot" should do the job) + and install Limnoria again. + For your information, Supybot's libraries are installed here: + %s\n""" % + (sys.executable, '\n '.join(supybot.__path__))) + exit(-1) + +from supybot.version import version + +def run(): + import supybot.log as log + import supybot.conf as conf + import supybot.world as world + import supybot.drivers as drivers + import supybot.ircmsgs as ircmsgs + import supybot.schedule as schedule + import supybot.httpserver as httpserver + # We schedule this event rather than have it actually run because if there + # is a failure between now and the time it takes the Owner plugin to load + # all the various plugins, our registry file might be wiped. That's bad. + interrupted = False + when = conf.supybot.upkeepInterval() + schedule.addPeriodicEvent(world.upkeep, when, name='upkeep', now=False) + world.startedAt = started + while world.ircs: + try: + drivers.run() + except KeyboardInterrupt: + if interrupted: + # Interrupted while waiting for queues to clear. Let's clear + # them ourselves. + for irc in world.ircs: + irc._reallyDie() + continue + else: + interrupted = True + log.info('Exiting due to Ctrl-C. ' + 'If the bot doesn\'t exit within a few seconds, ' + 'feel free to press Ctrl-C again to make it exit ' + 'without flushing its message queues.') + world.upkeep() + for irc in world.ircs: + quitmsg = conf.supybot.plugins.Owner.quitMsg() or \ + 'Ctrl-C at console.' + # Because we're quitting from the console, none of the + # standard msg substitutions exist, and these will show as + # raw strings by default. Substitute them here with + # something meaningful instead. + env = dict((key, '') + for key in ('who', 'nick', 'user', 'host')) + quitmsg = ircutils.standardSubstitute(irc, None, quitmsg, + env=env) + irc.queueMsg(ircmsgs.quit(quitmsg)) + irc.die() + except SystemExit as e: + s = str(e) + if s: + log.info('Exiting due to %s', s) + break + except: + try: # Ok, now we're *REALLY* paranoid! + log.exception('Exception raised out of drivers.run:') + except Exception as e: + print('Exception raised in log.exception. This is *really*') + print('bad. Hopefully it won\'t happen again, but tell us') + print('about it anyway, this is a significant problem.') + print('Anyway, here\'s the exception: %s' % \ + utils.gen.exnToString(e)) + except: + print('Oh, this really sucks. Not only did log.exception') + print('raise an exception, but freaking-a, it was a string') + print('exception. People who raise string exceptions should') + print('die a slow, painful death.') + httpserver.stopServer() + now = time.time() + seconds = now - world.startedAt + log.info('Total uptime: %s.', utils.gen.timeElapsed(seconds)) + (user, system, _, _, _) = os.times() + log.info('Total CPU time taken: %.2f seconds.', user+system) + log.info('No more Irc objects, exiting.') + +def main(): + parser = optparse.OptionParser(usage='Usage: %prog [options] configFile', + version='Limnoria %s running on Python %s' % + (version, sys.version)) + parser.add_option('-P', '--profile', action='store_true', dest='profile', + help='enables profiling') + parser.add_option('-n', '--nick', action='store', + dest='nick', default='', + help='nick the bot should use') + parser.add_option('-u', '--user', action='store', + dest='user', default='', + help='full username the bot should use') + parser.add_option('-i', '--ident', action='store', + dest='ident', default='', + help='ident the bot should use') + parser.add_option('-d', '--daemon', action='store_true', + dest='daemon', + help='Determines whether the bot will daemonize. ' + 'This is a no-op on non-POSIX systems.') + parser.add_option('', '--allow-default-owner', action='store_true', + dest='allowDefaultOwner', + help='Determines whether the bot will allow its ' + 'defaultCapabilities not to include "-owner", thus ' + 'giving all users the owner capability by default. ' + 'This is a security risk since it allows anyone to run ' + 'any command on your bot, so we advise not to use this.') + parser.add_option('', '--allow-root', action='store_true', + dest='allowRoot', + help='Determines whether the bot will be allowed to run ' + 'as root. This should not be used except in special ' + 'circumstances, such as running inside a containerized ' + 'environment.') + parser.add_option('', '--debug', action='store_true', dest='debug', + help='Determines whether some extra debugging stuff ' + 'will be logged in this script.') + parser.add_option('', '--disable-multiprocessing', action='store_true', + dest='disableMultiprocessing', + help='Disables multiprocessing stuff. May lead to ' + 'vulnerabilities.') + + (options, args) = parser.parse_args() + + if os.name == 'posix': + if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot: + sys.stderr.write('Running as root is not supported by default (see --allow-root).') + sys.stderr.write(os.linesep) + sys.exit(-1) + + if len(args) > 1: + parser.error("""Only one configuration file should be specified.""") + elif not args: + parser.error(utils.str.normalizeWhitespace("""It seems you've given me + no configuration file. If you do have a configuration file, be sure to + specify the filename. If you don't have a configuration file, read + docs/GETTING_STARTED and follow the instructions.""")) + else: + registryFilename = args.pop() + try: + # The registry *MUST* be opened before importing log or conf. + registry.open_registry(registryFilename) + shutil.copyfile(registryFilename, registryFilename + '.bak') + except registry.InvalidRegistryFile as e: + s = '%s in %s. Please fix this error and start supybot again.' % \ + (e, registryFilename) + s = textwrap.fill(s) + sys.stderr.write(s) + sys.stderr.write(os.linesep) + raise + sys.exit(-1) + except EnvironmentError as e: + sys.stderr.write(str(e)) + sys.stderr.write(os.linesep) + sys.exit(-1) + + i18n.getLocaleFromRegistryCache() + + try: + import supybot.log as log + except supybot.registry.InvalidRegistryValue as e: + # This is raised here because supybot.log imports supybot.conf. + name = e.value._name + errmsg = textwrap.fill('%s: %s' % (name, e), + width=78, subsequent_indent=' '*len(name)) + sys.stderr.write(errmsg) + sys.stderr.write(os.linesep) + sys.stderr.write('Please fix this error in your configuration file ' + 'and restart your bot.') + sys.stderr.write(os.linesep) + sys.exit(-1) + import supybot.conf as conf + import supybot.world as world + i18n.import_conf() + world.starting = True + + def closeRegistry(): + # We only print if world.dying so we don't see these messages during + # upkeep. + logger = log.debug + if world.dying: + logger = log.info + logger('Writing registry file to %s', registryFilename) + registry.close(conf.supybot, registryFilename) + logger('Finished writing registry file.') + world.flushers.append(closeRegistry) + world.registryFilename = registryFilename + + nick = options.nick or conf.supybot.nick() + user = options.user or conf.supybot.user() + ident = options.ident or conf.supybot.ident() + + networks = conf.supybot.networks() + if not networks: + questions.output("""No networks defined. Perhaps you should re-run the + wizard?""", fd=sys.stderr) + # XXX We should turn off logging here for a prettier presentation. + sys.exit(-1) + + if os.name == 'posix' and options.daemon: + def fork(): + child = os.fork() + if child != 0: + if options.debug: + print('Parent exiting, child PID: %s' % child) + # We must us os._exit instead of sys.exit so atexit handlers + # don't run. They shouldn't be dangerous, but they're ugly. + os._exit(0) + fork() + os.setsid() + # What the heck does this do? I wonder if it breaks anything... + # ...It did. I don't know why, but it seems largely useless. It seems + # to me reasonable that we should respect the user's umask. + #os.umask(0) + # Let's not do this for now (at least until I can make sure it works): + # Actually, let's never do this -- we'll always have files open in the + # bot directories, so they won't be able to be unmounted anyway. + # os.chdir('/') + fork() + # Since this is the indicator that no writing should be done to stdout, + # we'll set it to True before closing stdout et alii. + conf.daemonized = True + # Closing stdin shouldn't cause problems. We'll let it raise an + # exception if it does. + sys.stdin.close() + # Closing these two might cause problems; we log writes to them as + # level WARNING on upkeep. + sys.stdout.close() + sys.stderr.close() + sys.stdout = StringIO() + sys.stderr = StringIO() + # We have to be really methodical here. + os.close(0) + os.close(1) + os.close(2) + fd = os.open('/dev/null', os.O_RDWR) + os.dup2(fd, 0) + os.dup2(fd, 1) + os.dup2(fd, 2) + signal.signal(signal.SIGHUP, signal.SIG_IGN) + log.info('Completed daemonization. Current PID: %s', os.getpid()) + + # Stop setting our own umask. See comment above. + #os.umask(077) + + # Let's write the PID file. This has to go after daemonization, obviously. + pidFile = conf.supybot.pidFile() + if pidFile: + try: + fd = open(pidFile, 'w') + pid = os.getpid() + fd.write('%s%s' % (pid, os.linesep)) + fd.close() + def removePidFile(): + try: + os.remove(pidFile) + except EnvironmentError as e: + log.error('Could not remove pid file: %s', e) + atexit.register(removePidFile) + except EnvironmentError as e: + log.critical('Error opening/writing pid file %s: %s', pidFile, e) + sys.exit(-1) + + conf.allowDefaultOwner = options.allowDefaultOwner + world.disableMultiprocessing = options.disableMultiprocessing + + if not os.path.exists(conf.supybot.directories.log()): + os.mkdir(conf.supybot.directories.log()) + if not os.path.exists(conf.supybot.directories.conf()): + os.mkdir(conf.supybot.directories.conf()) + if not os.path.exists(conf.supybot.directories.data()): + os.mkdir(conf.supybot.directories.data()) + if not os.path.exists(conf.supybot.directories.data.tmp()): + os.mkdir(conf.supybot.directories.tmp()) + + userdataFilename = os.path.join(conf.supybot.directories.conf(), + 'userdata.conf') + # Let's open this now since we've got our directories setup. + if not os.path.exists(userdataFilename): + fd = open(userdataFilename, 'w') + fd.write('\n') + fd.close() + registry.open_registry(userdataFilename) + + import supybot.irclib as irclib + import supybot.ircmsgs as ircmsgs + import supybot.drivers as drivers + import supybot.callbacks as callbacks + import supybot.plugins.Owner as Owner + + # This may take some resources, and it does not need to run while booting, so + # we import it as late as possible (but before plugins are loaded). + import supybot.httpserver as httpserver + + owner = Owner.Class() + + if options.profile: + import profile + world.profiling = True + profile.run('run()', '%s-%i.prof' % (nick, time.time())) + else: + run() + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/scripts/limnoria_reset_password.py limnoria-2023.5.27/src/scripts/limnoria_reset_password.py --- limnoria-2023.1.28/src/scripts/limnoria_reset_password.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_reset_password.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2002-2004, Jeremiah Fincher +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + + + +import supybot + +from supybot.questions import * + +import os +import sys +import optparse + +def _main(): + import supybot.log as log + import supybot.conf as conf + conf.supybot.log.stdout.setValue(False) + parser = optparse.OptionParser(usage='Usage: %prog [options] ', + version='supybot %s' % conf.version) + parser.add_option('-u', '--username', action='store', default='', + dest='name', + help='username for the user.') + parser.add_option('-p', '--password', action='store', default='', + dest='password', + help='new password for the user.') + (options, args) = parser.parse_args() + if len(args) != 1: + parser.error('Specify the users.conf file you\'d like to use. ' + 'Be sure *not* to specify your registry file, generated ' + 'by supybot-wizard. This is not the file you want. ' + 'Instead, take a look in your conf directory (usually ' + 'named "conf") and take a gander at the file ' + '"users.conf". That\'s the one you want.') + + filename = os.path.abspath(args[0]) + conf.supybot.directories.log.setValue('/') + conf.supybot.directories.conf.setValue('/') + conf.supybot.directories.data.setValue('/') + conf.supybot.directories.plugins.setValue(['/']) + conf.supybot.databases.users.filename.setValue(filename) + import supybot.ircdb as ircdb + + if not options.name: + name = '' + while not name: + name = something('What is the user\'s name?') + try: + # Check to see if the user is already in the database. + _ = ircdb.users.getUser(name) + # Success! + except KeyError: + # Failure. No such user exists. Try another name. + output('That user doesn\'t exist. Try another name.') + name = '' + else: + try: + _ = ircdb.users.getUser(options.name) + name = options.name + except KeyError: + # Same as above. We exit here instead. + output('That user doesn\'t exist. Try another name.') + sys.exit(-1) + + if not options.password: + password = getpass('Please enter new password for %s: ' % name) + else: + password = options.password + + user = ircdb.users.getUser(name) + user.setPassword(password) + ircdb.users.setUser(user) + ircdb.users.flush() + ircdb.users.close() + print('User %s\'s password reset!' % name) + +def main(): + try: + main() + except KeyboardInterrupt: + pass + +if __name__ == '__main__': + main() + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/scripts/limnoria_test.py limnoria-2023.5.27/src/scripts/limnoria_test.py --- limnoria-2023.1.28/src/scripts/limnoria_test.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_test.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2002-2005, Jeremiah Fincher +# Copyright (c) 2011, James McCoy +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +import os +import sys +import time +import shutil +import fnmatch +started = time.time() + +import supybot +import logging +import traceback + +# We need to do this before we import conf. +if not os.path.exists('test-conf'): + os.mkdir('test-conf') + +registryFilename = os.path.join('test-conf', 'test.conf') +fd = open(registryFilename, 'w') +fd.write(""" +supybot.directories.data: %(base_dir)s/test-data +supybot.directories.conf: %(base_dir)s/test-conf +supybot.directories.log: %(base_dir)s/test-logs +supybot.reply.whenNotCommand: True +supybot.log.stdout: False +supybot.log.stdout.level: ERROR +supybot.log.level: DEBUG +supybot.log.format: %%(levelname)s %%(message)s +supybot.log.plugins.individualLogfiles: False +supybot.protocols.irc.throttleTime: 0 +supybot.reply.whenAddressedBy.chars: @ +supybot.networks.test.server: should.not.need.this +supybot.networks.testnet1.server: should.not.need.this +supybot.networks.testnet2.server: should.not.need.this +supybot.networks.testnet3.server: should.not.need.this +supybot.nick: test +supybot.databases.users.allowUnregistration: True +""" % {'base_dir': os.getcwd()}) +fd.close() + +import supybot.registry as registry +registry.open_registry(registryFilename) + +import supybot.log as log +import supybot.conf as conf +conf.allowEval = True +conf.supybot.flush.setValue(False) + +import re +import sys +import glob +import atexit +import os.path +import unittest + +import supybot.utils as utils +import supybot.world as world +import supybot.callbacks as callbacks +world.startedAt = started + +import logging +class TestLogFilter(logging.Filter): + bads = [ + 'No callbacks in', + 'Invalid channel database', + 'Exact error', + 'Invalid user dictionary', + 'because of noFlush', + 'Queuing NICK', + 'Queuing USER', + 'IgnoresDB.reload failed', + 'Starting log for', + 'Irc object for test dying', + 'Last Irc,', + ] + def filter(self, record): + for bad in self.bads: + if bad in record.msg: + return False + return True +log._logger.addFilter(TestLogFilter()) + +class path(str): + """A class to represent platform-independent paths.""" + _r = re.compile(r'[\\/]') + def __hash__(self): + return reduce(lambda h, s: h ^ hash(s), self._r.split(self), 0) + def __eq__(self, other): + return self._r.split(self) == self._r.split(other) + +def main(): + import glob + import os.path + import optparse + import supybot.test as test + import supybot.plugin as plugin + + parser = optparse.OptionParser(usage='Usage: %prog [options] [plugins]', + version='Supybot %s' % conf.version) + parser.add_option('-c', '--clean', action='store_true', default=False, + dest='clean', help='Cleans the various data/conf/logs' + 'directories before running tests.') + parser.add_option('-t', '--timeout', action='store', type='float', + dest='timeout', + help='Sets the timeout, in seconds, for tests to return ' + 'responses.') + parser.add_option('-v', '--verbose', action='count', default=0, + help='Increase verbosity, logging extra information ' + 'about each test that runs.') + parser.add_option('', '--fail-fast', action='store_true', default=False, + help='Stop at first failed test.') + parser.add_option('', '--no-network', action='store_true', default=False, + dest='nonetwork', help='Causes the network-based tests ' + 'not to run.') + parser.add_option('', '--no-setuid', action='store_true', default=False, + dest='nosetuid', help='Causes the tests based on a ' + 'setuid executable not to run.') + parser.add_option('', '--trace', action='store_true', default=False, + help='Traces all calls made. Unless you\'re really in ' + 'a pinch, you probably shouldn\'t do this; it results ' + 'in copious amounts of output.') + parser.add_option('', '--plugins-dir', + action='append', dest='pluginsDirs', default=[], + help='Looks in in the given directory for plugins and ' + 'loads the tests from all of them.') + parser.add_option('', '--exclude', + action='append', dest='excludePlugins', default=[], + help='List of plugins you do not want --plugins-dir ' + 'to include.') + parser.add_option('', '--disable-multiprocessing', action='store_true', + dest='disableMultiprocessing', + help='Disables multiprocessing stuff.') + (options, args) = parser.parse_args() + + world.disableMultiprocessing = options.disableMultiprocessing + + # This must go before checking for args, of course. + if options.excludePlugins: + exclusionPattern = re.compile('|'.join( + fnmatch.translate(pat) for pat in options.excludePlugins + )) + else: + exclusionPattern = None + for pluginDir in options.pluginsDirs: + for name in glob.glob(os.path.join(pluginDir, '*')): + if (exclusionPattern is None or not exclusionPattern.match(name)) \ + and os.path.isdir(name): + args.append(name) + + if not args: + parser.print_help() + sys.exit(-1) + + if options.timeout: + test.timeout = options.timeout + + if options.trace: + traceFilename = conf.supybot.directories.log.dirize('trace.log') + fd = open(traceFilename, 'w') + sys.settrace(utils.gen.callTracer(fd)) + atexit.register(fd.close) + atexit.register(lambda : sys.settrace(None)) + + world.myVerbose = options.verbose + + if options.nonetwork: + test.network = False + if options.nosetuid: + test.setuid = False + + log.testing = True + world.testing = True + + args = [s.rstrip('\\/') for s in args] + pluginDirs = set([os.path.dirname(s) or '.' for s in args]) + conf.supybot.directories.plugins.setValue(list(pluginDirs)) + pluginNames = set([os.path.basename(s) for s in args]) + load = unittest.defaultTestLoader.loadTestsFromModule + for pluginName in pluginNames: + if pluginName.endswith('.py'): + pluginName = pluginName[:-3] + try: + pluginModule = plugin.loadPluginModule(pluginName) + except (ImportError, callbacks.Error) as e: + sys.stderr.write('Failed to load plugin %s:' % pluginName) + traceback.print_exc() + sys.stderr.write('(pluginDirs: %s)\n' % + conf.supybot.directories.plugins()) + continue + if hasattr(pluginModule, 'test'): + test.suites.append(load(pluginModule.test)) + + suite = unittest.TestSuite(test.suites) + if options.fail_fast: + runner = unittest.TextTestRunner(verbosity=2, + failfast=True) + else: + runner = unittest.TextTestRunner(verbosity=2) + print('Testing began at %s (pid %s)' % (time.ctime(), os.getpid())) + if options.clean: + shutil.rmtree(conf.supybot.directories.log()) + log._mkDirs() + shutil.rmtree(conf.supybot.directories.conf()) + shutil.rmtree(conf.supybot.directories.data()) + result = runner.run(suite) + + if hasattr(unittest, 'asserts'): + print('Total asserts: %s' % unittest.asserts) + + if result.wasSuccessful(): + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == '__main__': + main() + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/scripts/limnoria_wizard.py limnoria-2023.5.27/src/scripts/limnoria_wizard.py --- limnoria-2023.1.28/src/scripts/limnoria_wizard.py 1970-01-01 00:00:00.000000000 +0000 +++ limnoria-2023.5.27/src/scripts/limnoria_wizard.py 2023-05-18 13:06:27.000000000 +0000 @@ -0,0 +1,836 @@ +#!/usr/bin/env python3 + +### +# Copyright (c) 2003-2004, Jeremiah Fincher +# Copyright (c) 2009, James McCoy +# Copyright (c) 2010-2021, Valentin Lorentz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +from __future__ import print_function + +import os +import sys + +def error(s): + sys.stderr.write(s) + if not s.endswith(os.linesep): + sys.stderr.write(os.linesep) + sys.exit(-1) + +import supybot + +import re +import time +import pydoc +import pprint +import socket +import logging +import optparse + +try: + import supybot.i18n as i18n +except ImportError: + sys.stderr.write("""Error: + You are running a mix of Limnoria and stock Supybot code. Although you run + one of Limnoria\'s executables, Python tries to load stock + Supybot\'s library. To fix this issue, uninstall Supybot + ("%s -m pip uninstall supybot" should do the job) + and install Limnoria again. + For your information, Supybot's libraries are installed here: + %s\n""" % + (sys.executable, '\n '.join(supybot.__path__))) + exit(-1) + +import supybot.ansi as ansi +import supybot.utils as utils +import supybot.ircutils as ircutils +import supybot.registry as registry +# supybot.plugin, supybot.log, and supybot.conf will be imported later, +# because we need to set a language before loading the conf + +import supybot.questions as questions +from supybot.questions import output, yn, anything, something, expect, getpass + +def getPlugins(pluginDirs): + plugins = set([]) + join = os.path.join + for pluginDir in pluginDirs: + try: + for filename in os.listdir(pluginDir): + fname = join(pluginDir, filename) + if (filename.endswith('.py') or os.path.isdir(fname)) \ + and filename[0].isupper(): + plugins.add(os.path.splitext(filename)[0]) + except OSError: + continue + plugins.discard('Owner') + plugins = list(plugins) + plugins.sort() + return plugins + +def loadPlugin(name): + import supybot.plugin as plugin + try: + module = plugin.loadPluginModule(name) + if hasattr(module, 'Class'): + return module + else: + output("""That plugin loaded fine, but didn't seem to be a real + Supybot plugin; there was no Class variable to tell us what class + to load when we load the plugin. We'll skip over it for now, but + you can always add it later.""") + return None + except Exception as e: + output("""We encountered a bit of trouble trying to load plugin %r. + Python told us %r. We'll skip over it for now, you can always add it + later.""" % (name, utils.gen.exnToString(e))) + return None + +def describePlugin(module, showUsage): + if module.__doc__: + output(module.__doc__, unformatted=False) + elif hasattr(module.Class, '__doc__'): + output(module.Class.__doc__, unformatted=False) + else: + output("""Unfortunately, this plugin doesn't seem to have any + documentation. Sorry about that.""") + if showUsage: + if hasattr(module, 'example'): + if yn('This plugin has a usage example. ' + 'Would you like to see it?', default=False): + pydoc.pager(module.example) + else: + output("""This plugin has no usage example.""") + +def clearLoadedPlugins(plugins, pluginRegistry): + for plugin in plugins: + try: + pluginKey = pluginRegistry.get(plugin) + if pluginKey(): + plugins.remove(plugin) + except registry.NonExistentRegistryEntry: + continue + +_windowsVarRe = re.compile(r'%(\w+)%') +def getDirectoryName(default, basedir=os.curdir, prompt=True): + done = False + while not done: + if prompt: + dir = something('What directory do you want to use?', + default=os.path.join(basedir, default)) + else: + dir = os.path.join(basedir, default) + orig_dir = dir + dir = os.path.expanduser(dir) + dir = _windowsVarRe.sub(r'$\1', dir) + dir = os.path.expandvars(dir) + dir = os.path.abspath(dir) + try: + os.makedirs(dir) + done = True + except OSError as e: + # 17 is File exists for Linux (and likely other POSIX systems) + # 183 is the same for Windows + if e.args[0] == 17 or (os.name == 'nt' and e.args[0] == 183): + done = True + else: + output("""Sorry, I couldn't make that directory for some + reason. The Operating System told me %s. You're going to + have to pick someplace else.""" % e) + prompt = True + return (dir, os.path.dirname(orig_dir)) + + +def _main(): + import supybot.version as version + parser = optparse.OptionParser(usage='Usage: %prog [options]', + version='Supybot %s' % version.version) + parser.add_option('', '--allow-root', action='store_true', + dest='allowRoot', + help='Determines whether the wizard will be allowed to ' + 'as root. This should not be used except in special ' + 'circumstances, such as running inside a containerized ' + 'environment.') + parser.add_option('', '--allow-home', action='store_true', + dest='allowHome', + help='Determines whether the wizard will be allowed to ' + 'run directly in the HOME directory. ' + 'You should not do this unless you want it to ' + 'create multiple files in your HOME directory.') + parser.add_option('', '--allow-bin', action='store_true', + dest='allowBin', + help='Determines whether the wizard will be allowed to ' + 'run directly in a directory for binaries (eg. ' + '/usr/bin, /usr/local/bin, or ~/.local/bin). ' + 'You should not do this unless you want it to ' + 'create multiple non-binary files in directory ' + 'that should only contain binaries.') + parser.add_option('', '--no-network', action='store_false', + dest='network', + help='Determines whether the wizard will be allowed to ' + 'run without a network connection.') + (options, args) = parser.parse_args() + if os.name == 'posix': + if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot: + error('Running as root is not supported by default (see --allow-root).') + if os.name == 'posix': + if (os.getcwd() == os.path.expanduser('~')) and not options.allowHome: + error('Please, don\'t run this in your HOME directory.') + if (os.path.split(os.getcwd())[-1] == 'bin') and not options.allowBin: + error('Please, don\'t run this in a "bin" directory.') + if os.path.isfile(os.path.join('scripts', 'supybot-wizard')) or \ + os.path.isfile(os.path.join('..', 'scripts', 'supybot-wizard')): + print('') + print('+------------------------------------------------------------+') + print('| +--------------------------------------------------------+ |') + print('| | Warning: It looks like you are running the wizard from | |') + print('| | the Supybot source directory. This is not recommended. | |') + print('| | Please press Ctrl-C and change to another directory. | |') + print('| +--------------------------------------------------------+ |') + print('+------------------------------------------------------------+') + print('') + + if args: + parser.error('This program takes no non-option arguments.') + output("""This is a wizard to help you start running supybot. What it + will do is create the necessary config files based on the options you + select here. So hold on tight and be ready to be interrogated :)""") + + + output("""First of all, we can bold the questions you're asked so you can + easily distinguish the mostly useless blather (like this) from the + questions that you actually have to answer.""") + if yn('Would you like to try this bolding?', default=True): + questions.useBold = True + if not yn('Do you see this in bold?'): + output("""Sorry, it looks like your terminal isn't ANSI compliant. + Try again some other day, on some other terminal :)""") + questions.useBold = False + else: + output("""Great!""") + + ### + # Preliminary questions. + ### + output("""We've got some preliminary things to get out of the way before + we can really start asking you questions that directly relate to what your + bot is going to be like.""") + + # Advanced? + output("""We want to know if you consider yourself an advanced Supybot + user because some questions are just utterly boring and useless for new + users. Others might not make sense unless you've used Supybot for some + time.""") + advanced = yn('Are you an advanced Supybot user?', default=False) + + # Language? + output("""This version of Supybot (known as Limnoria) includes another + language. This can be changed at any time. You need to answer with a short + id for the language, such as 'en', 'fr', 'it' (without the quotes). If + you want to use English, just press enter.""") + language = something('What language do you want to use?', default='en') + + class Empty: + """This is a hack to allow the i18n to get the current language, before + loading the conf module, before the conf module needs i18n to set the + default strings.""" + def __call__(self): + return self.value + fakeConf = Empty() + fakeConf.supybot = Empty() + fakeConf.supybot.language = Empty() + fakeConf.supybot.language.value = language + i18n.conf = fakeConf + i18n.currentLocale = language + i18n.reloadLocales() + import supybot.conf as conf + i18n.import_conf() # It imports the real conf module + + ### Directories. + # We set these variables in cache because otherwise conf and log will + # create directories for the default values, which might not be what the + # user wants. + if advanced: + output("""Now we've got to ask you some questions about where some of + your directories are (or, perhaps, will be :)). If you're running this + wizard from the directory you'll actually be starting your bot from and + don't mind creating some directories in the current directory, then + just don't give answers to these questions and we'll create the + directories we need right here in this directory.""") + + # conf.supybot.directories.log + output("""Your bot will need to put its logs somewhere. Do you have + any specific place you'd like them? If not, just press enter and we'll + make a directory named "logs" right here.""") + (logDir, basedir) = getDirectoryName('logs') + conf.supybot.directories.log.setValue(logDir) + import supybot.log as log + log._stdoutHandler.setLevel(100) # *Nothing* gets through this! + + # conf.supybot.directories.data + output("""Your bot will need to put various data somewhere. Things + like databases, downloaded files, etc. Do you have any specific place + you'd like the bot to put these things? If not, just press enter and + we'll make a directory named "data" right here.""") + (dataDir, basedir) = getDirectoryName('data', basedir=basedir) + conf.supybot.directories.data.setValue(dataDir) + + # conf.supybot.directories.conf + output("""Your bot must know where to find its configuration files. + It'll probably only make one or two, but it's gotta have some place to + put them. Where should that place be? If you don't care, just press + enter and we'll make a directory right here named "conf" where it'll + store its stuff. """) + (confDir, basedir) = getDirectoryName('conf', basedir=basedir) + conf.supybot.directories.conf.setValue(confDir) + + # conf.supybot.directories.backup + output("""Your bot must know where to place backups of its conf and + data files. Where should that place be? If you don't care, just press + enter and we'll make a directory right here named "backup" where it'll + store its stuff.""") + (backupDir, basedir) = getDirectoryName('backup', basedir=basedir) + conf.supybot.directories.backup.setValue(backupDir) + + # conf.supybot.directories.data.tmp + output("""Your bot needs a directory to put temporary files (used + mainly to atomically update its configuration files).""") + (tmpDir, basedir) = getDirectoryName('tmp', basedir=basedir) + conf.supybot.directories.data.tmp.setValue(tmpDir) + + # conf.supybot.directories.data.web + output("""Your bot needs a directory to put files related to the web + server (templates, CSS).""") + (webDir, basedir) = getDirectoryName('web', basedir=basedir) + conf.supybot.directories.data.web.setValue(webDir) + + + # imports callbacks, which imports ircdb, which requires + # directories.conf + import supybot.plugin as plugin + + # pluginDirs + output("""Your bot will also need to know where to find its plugins at. + Of course, it already knows where the plugins that it came with are, + but your own personal plugins that you write for will probably be + somewhere else.""") + pluginDirs = conf.supybot.directories.plugins() + output("""Currently, the bot knows about the following directories:""") + output(format('%L', pluginDirs + [plugin._pluginsDir])) + while yn('Would you like to add another plugin directory? ' + 'Adding a local plugin directory is good style.', + default=True): + (pluginDir, _) = getDirectoryName('plugins', basedir=basedir) + if pluginDir not in pluginDirs: + pluginDirs.append(pluginDir) + conf.supybot.directories.plugins.setValue(pluginDirs) + else: + output("""Your bot needs to create some directories in order to store + the various log, config, and data files.""") + basedir = something("""Where would you like to create these + directories?""", default=os.curdir) + + # conf.supybot.directories.log + (logDir, basedir) = getDirectoryName('logs', + basedir=basedir, prompt=False) + conf.supybot.directories.log.setValue(logDir) + + # conf.supybot.directories.data + (dataDir, basedir) = getDirectoryName('data', + basedir=basedir, prompt=False) + conf.supybot.directories.data.setValue(dataDir) + (tmpDir, basedir) = getDirectoryName('tmp', + basedir=basedir, prompt=False) + conf.supybot.directories.data.tmp.setValue(tmpDir) + (webDir, basedir) = getDirectoryName('web', + basedir=basedir, prompt=False) + conf.supybot.directories.data.web.setValue(webDir) + + # conf.supybot.directories.conf + (confDir, basedir) = getDirectoryName('conf', + basedir=basedir, prompt=False) + conf.supybot.directories.conf.setValue(confDir) + + # conf.supybot.directories.backup + (backupDir, basedir) = getDirectoryName('backup', + basedir=basedir, prompt=False) + conf.supybot.directories.backup.setValue(backupDir) + + # pluginDirs + pluginDirs = conf.supybot.directories.plugins() + (pluginDir, _) = getDirectoryName('plugins', + basedir=basedir, prompt=False) + if pluginDir not in pluginDirs: + pluginDirs.append(pluginDir) + conf.supybot.directories.plugins.setValue(pluginDirs) + + import supybot.log as log + log._stdoutHandler.setLevel(100) # *Nothing* gets through this! + import supybot.plugin as plugin + + output("Good! We're done with the directory stuff.") + + ### + # Bot stuff + ### + output("""Now we're going to ask you things that actually relate to the + bot you'll be running.""") + + network = None + while not network: + output("""First, we need to know the name of the network you'd like to + connect to. Not the server host, mind you, but the name of the + network. If you plan to connect to irc.libera.chat, for instance, + you should answer this question with 'libera' or 'liberachat' + (without the quotes). + """) + network = something('What IRC network will you be connecting to?') + if '.' in network: + output("""There shouldn't be a '.' in the network name. Remember, + this is the network name, not the actual server you plan to connect + to.""") + network = None + elif not registry.isValidRegistryName(network): + output("""That's not a valid name for one reason or another. Please + pick a simpler name, one more likely to be valid.""") + network = None + + conf.supybot.networks.setValue([network]) + network = conf.registerNetwork(network) + + defaultServer = None + server = None + ip = None + while not ip: + serverString = something('What server would you like to connect to?', + default=defaultServer) + if options.network: + try: + output("""Looking up %s...""" % serverString) + ip = socket.gethostbyname(serverString) + except: + output("""Sorry, I couldn't find that server. Perhaps you + misspelled it? Also, be sure not to put the port in the + server's name -- we'll ask you about that later.""") + else: + ip = 'no network available' + + output("""Found %s (%s).""" % (serverString, ip)) + + # conf.supybot.networks..ssl + output("""Most networks allow you to use a secure connection via SSL. + If you are not sure whether this network supports SSL or not, check + its website.""") + use_ssl = yn('Do you want to use an SSL connection?', default=True) + network.ssl.setValue(use_ssl) + + output("""IRC servers almost always accept connections on port + 6697 (or 6667 when not using SSL). They can, however, accept connections + anywhere their admins feel like they want to accept connections from.""") + if yn('Does this server require connection on a non-standard port?', + default=False): + port = 0 + while not port: + port = something('What port is that?') + try: + i = int(port) + if not (0 < i < 65536): + raise ValueError() + except ValueError: + output("""That's not a valid port.""") + port = 0 + else: + if network.ssl.value: + port = 6697 + else: + port = 6667 + server = ':'.join([serverString, str(port)]) + network.servers.setValue([server]) + + # conf.supybot.nick + # Force the user into specifying a nick if it didn't have one already + while True: + nick = something('What nick would you like your bot to use?', + default=None) + try: + conf.supybot.nick.set(nick) + break + except registry.InvalidRegistryValue: + output("""That's not a valid nick. Go ahead and pick another.""") + + # conf.supybot.networks..sasl.{username,password} + output("""Most networks allow you to create an account, using services + typically known as NickServ. If you already have one, you can configure + the bot to use that account by proving your username and password + (also known as "SASL plain"). + It is usually not a requirement.""") + if yn('Do you want your bot to use a network account?', default=False): + while True: + username = something('What account name should the bot use?') + password = getpass('What is the password of that account?') + if not username: + if yn('Missing username. Do you want to try again?'): + continue + if not password: + if yn('Missing password. Do you want to try again?'): + continue + network.sasl.username.set(username) + network.sasl.password.set(password) + break + + # conf.supybot.user + if advanced: + output("""If you've ever done a /whois on a person, you know that IRC + provides a way for users to show the world their full name. What would + you like your bot's full name to be? If you don't care, just press + enter and it'll be the same as your bot's nick.""") + user = '' + user = something('What would you like your bot\'s full name to be?', + default=nick) + conf.supybot.user.set(user) + # conf.supybot.ident (if advanced) + defaultIdent = 'limnoria' + if advanced: + output("""IRC servers also allow you to set your ident, which they + might need if they can't find your identd server. What would you like + your ident to be? If you don't care, press enter and we'll use + 'limnoria'. In fact, we prefer that you do this, because it provides + free advertising for Supybot when users /whois your bot. But, of + course, it's your call.""") + while True: + ident = something('What would you like your bot\'s ident to be?', + default=defaultIdent) + try: + conf.supybot.ident.set(ident) + break + except registry.InvalidRegistryValue: + output("""That was not a valid ident. Go ahead and pick + another.""") + else: + conf.supybot.ident.set(defaultIdent) + + # conf.supybot.networks..password + output("""Some servers require a password to connect to them. Most + public servers don't. If you try to connect to a server and for some + reason it just won't work, it might be that you need to set a + password.""") + if yn('Do you want to set such a password?', default=False): + network.password.set(getpass()) + + # conf.supybot.networks..channels + output("""Of course, having an IRC bot isn't the most useful thing in the + world unless you can make that bot join some channels.""") + if yn('Do you want your bot to join some channels when it connects?', + default=True): + defaultChannels = ' '.join(network.channels()) + output("""Separate channels with spaces. If the channel is locked + with a key, follow the channel name with the key separated + by a comma. For example: + #supybot-bots #mychannel,mykey #otherchannel"""); + while True: + channels_input = something('What channels?', default=defaultChannels) + channels = [] + error_ = False + for channel in channels_input.split(): + L = channel.split(',') + if len(L) == 0: + continue + elif len(L) == 1: + (channel,) = L + channels.append(channel) + elif len(L) == 2: + (channel, key) = L + channels.append(channel) + network.channels.key.get(channel).setValue(key) + else: + output("""Too many commas in %s.""" % channel) + error_ = True + break + if error_: + break + try: + network.channels.set(' '.join(channels)) + break + except registry.InvalidRegistryValue as e: + output(""""%s" is an invalid IRC channel. Be sure to prefix + the channel with # (or +, or !, or &, but no one uses those + channels, really). Be sure the channel key (if you are + supplying one) does not contain a comma.""" % e.channel) + else: + network.channels.setValue([]) + + ### + # Plugins + ### + def configurePlugin(module, advanced): + if hasattr(module, 'configure'): + output("""Beginning configuration for %s...""" % + module.Class.__name__) + module.configure(advanced) + print() # Blank line :) + output("""Done!""") + else: + conf.registerPlugin(module.__name__, currentValue=True) + + plugins = getPlugins(pluginDirs + [plugin._pluginsDir]) + for s in ('Admin', 'User', 'Channel', 'Misc', 'Config', 'Utilities'): + m = loadPlugin(s) + if m is not None: + configurePlugin(m, advanced) + else: + error('There was an error loading one of the core plugins that ' + 'under almost all circumstances are loaded. Go ahead and ' + 'fix that error and run this script again.') + clearLoadedPlugins(plugins, conf.supybot.plugins) + + output("""Now we're going to run you through plugin configuration. There's + a variety of plugins in supybot by default, but you can create and + add your own, of course. We'll allow you to take a look at the known + plugins' descriptions and configure them + if you like what you see.""") + + # bulk + addedBulk = False + if advanced and yn('Would you like to add plugins en masse first?'): + addedBulk = True + output(format("""The available plugins are: %L.""", plugins)) + output("""What plugins would you like to add? If you've changed your + mind and would rather not add plugins in bulk like this, just press + enter and we'll move on to the individual plugin configuration. + We suggest you to add Aka, Ctcp, Later, Network, Plugin, String, + and Utilities""") + massPlugins = anything('Separate plugin names by spaces or commas:') + for name in re.split(r',?\s+', massPlugins): + module = loadPlugin(name) + if module is not None: + configurePlugin(module, advanced) + clearLoadedPlugins(plugins, conf.supybot.plugins) + + # individual + if yn('Would you like to look at plugins individually?'): + output("""Next comes your opportunity to learn more about the plugins + that are available and select some (or all!) of them to run in your + bot. Before you have to make a decision, of course, you'll be able to + see a short description of the plugin and, if you choose, an example + session with the plugin. Let's begin.""") + # until we get example strings again, this will default to false + #showUsage =yn('Would you like the option of seeing usage examples?') + showUsage = False + name = expect('What plugin would you like to look at?', + plugins, acceptEmpty=True) + while name: + module = loadPlugin(name) + if module is not None: + describePlugin(module, showUsage) + if yn('Would you like to load this plugin?', default=True): + configurePlugin(module, advanced) + clearLoadedPlugins(plugins, conf.supybot.plugins) + if not yn('Would you like add another plugin?'): + break + name = expect('What plugin would you like to look at?', plugins) + + ### + # Sundry + ### + output("""Although supybot offers a supybot-adduser script, with which + you can add users to your bot's user database, it's *very* important that + you have an owner user for you bot.""") + if yn('Would you like to add an owner user for your bot?', default=True): + import supybot.ircdb as ircdb + name = something('What should the owner\'s username be?') + try: + id = ircdb.users.getUserId(name) + u = ircdb.users.getUser(id) + if u._checkCapability('owner'): + output("""That user already exists, and has owner capabilities + already. Perhaps you added it before? """) + if yn('Do you want to remove its owner capability?', + default=False): + u.removeCapability('owner') + ircdb.users.setUser(u) + else: + output("""That user already exists, but doesn't have owner + capabilities.""") + if yn('Do you want to add to it owner capabilities?', + default=False): + u.addCapability('owner') + ircdb.users.setUser(u) + except KeyError: + password = getpass('What should the owner\'s password be?') + u = ircdb.users.newUser() + u.name = name + u.setPassword(password) + u.addCapability('owner') + ircdb.users.setUser(u) + + output("""Of course, when you're in an IRC channel you can address the bot + by its nick and it will respond, if you give it a valid command (it may or + may not respond, depending on what your config variable replyWhenNotCommand + is set to). But your bot can also respond to a short "prefix character," + so instead of saying "bot: do this," you can say, "@do this" and achieve + the same effect. Of course, you don't *have* to have a prefix char, but + if the bot ends up participating significantly in your channel, it'll ease + things.""") + if yn('Would you like to set the prefix char(s) for your bot? ', + default=True): + output("""Enter any characters you want here, but be careful: they + should be rare enough that people don't accidentally address the bot + (simply because they'll probably be annoyed if they do address the bot + on accident). You can even have more than one. I (jemfinch) am quite + partial to @, but that's because I've been using it since my ocamlbot + days.""") + import supybot.callbacks as callbacks + c = '' + while not c: + try: + c = anything('What would you like your bot\'s prefix ' + 'character(s) to be?') + conf.supybot.reply.whenAddressedBy.chars.set(c) + except registry.InvalidRegistryValue as e: + output(str(e)) + c = '' + else: + conf.supybot.reply.whenAddressedBy.chars.set('') + + ### + # logging variables. + ### + + if advanced: + # conf.supybot.log.stdout + output("""By default, your bot will log not only to files in the logs + directory you gave it, but also to stdout. We find this useful for + debugging, and also just for the pretty output (it's colored!)""") + stdout = not yn('Would you like to turn off this logging to stdout?', + default=False) + conf.supybot.log.stdout.setValue(stdout) + if conf.supybot.log.stdout(): + # conf.something + output("""Some terminals may not be able to display the pretty + colors logged to stderr. By default, though, we turn the colors + off for Windows machines and leave it on for *nix machines.""") + if os.name != 'nt': + conf.supybot.log.stdout.colorized.setValue( + not yn('Would you like to turn this colorization off?', + default=False)) + + # conf.supybot.log.level + output("""Your bot can handle debug messages at several priorities, + CRITICAL, ERROR, WARNING, INFO, and DEBUG, in decreasing order of + priority. By default, your bot will log all of these priorities except + DEBUG. You can, however, specify that it only log messages above a + certain priority level.""") + priority = str(conf.supybot.log.level) + logLevel = something('What would you like the minimum priority to be?' + ' Just press enter to accept the default.', + default=priority).lower() + while logLevel not in ['debug','info','warning','error','critical']: + output("""That's not a valid priority. Valid priorities include + 'DEBUG', 'INFO', 'WARNING', 'ERROR', and 'CRITICAL'""") + logLevel = something('What would you like the minimum priority to ' + 'be? Just press enter to accept the default.', + default=priority).lower() + conf.supybot.log.level.set(logLevel) + + # conf.supybot.databases.plugins.channelSpecific + + output("""Many plugins in Supybot are channel-specific. Their + databases, likewise, are specific to each channel the bot is in. Many + people don't want this, so we have one central location in which to + say that you would prefer all databases for all channels to be shared. + This variable, supybot.databases.plugins.channelSpecific, is that + place.""") + + conf.supybot.databases.plugins.channelSpecific.setValue( + not yn('Would you like plugin databases to be shared by all ' + 'channels, rather than specific to each channel the ' + 'bot is in?')) + + output("""There are a lot of options we didn't ask you about simply + because we'd rather you get up and running and have time + left to play around with your bot. But come back and see + us! When you've played around with your bot enough to + know what you like, what you don't like, what you'd like + to change, then take a look at your configuration file + when your bot isn't running and read the comments, + tweaking values to your heart's desire.""") + + # Let's make sure that src/ plugins are loaded. + conf.registerPlugin('Admin', True) + conf.registerPlugin('AutoMode', True) + conf.registerPlugin('Channel', True) + conf.registerPlugin('Config', True) + conf.registerPlugin('Misc', True) + conf.registerPlugin('Network', True) + conf.registerPlugin('NickAuth', True) + conf.registerPlugin('User', True) + conf.registerPlugin('Utilities', True) + + ### + # Write the registry + ### + + # We're going to need to do a darcs predist thing here. + #conf.supybot.debug.generated.setValue('...') + + if advanced: + basedir = '.' + filename = os.path.join(basedir, '%s.conf') + + filename = something("""In which file would you like to save + this config?""", default=filename % nick) + if not filename.endswith('.conf'): + filename += '.conf' + registry.close(conf.supybot, os.path.expanduser(filename)) + + # Done! + output("""All done! Your new bot configuration is %s. If you're running + a *nix based OS, you can probably start your bot with the command line + "supybot %s". If you're not running a *nix or similar machine, you'll + just have to start it like you start all your other Python scripts.""" % \ + (filename, filename)) + +def main(): + try: + _main() + except KeyboardInterrupt: + # We may still be using bold text when exiting during a prompt + if questions.useBold: + import supybot.ansi as ansi + print(ansi.RESET) + print() + print() + output("""Well, it looks like you canceled out of the wizard before + it was done. Unfortunately, I didn't get to write anything to file. + Please run the wizard again to completion.""") + + +if __name__ == '__main__': + main() + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.1.28/src/version.py limnoria-2023.5.27/src/version.py --- limnoria-2023.1.28/src/version.py 2023-02-11 09:15:17.000000000 +0000 +++ limnoria-2023.5.27/src/version.py 2023-05-27 06:02:35.000000000 +0000 @@ -1,4 +1,4 @@ -version = '2023.01.28' +version = '2023.05.27' try: # For import from setup.py import supybot.utils.python supybot.utils.python._debug_software_version = version