--- gozerbot-0.99.1.orig/setup.cfg +++ gozerbot-0.99.1/setup.cfg @@ -2,4 +2,3 @@ tag_build = tag_date = 0 tag_svn_revision = 0 - --- gozerbot-0.99.1.orig/debian/gozerbot.postrm +++ gozerbot-0.99.1/debian/gozerbot.postrm @@ -0,0 +1,46 @@ +#!/bin/sh +# postrm script for gozerbot +# +# see: dh_installdeb(1) +. /usr/share/debconf/confmodule + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + deluser --quiet --system gozerbot > /dev/null || true + delgroup --quiet --system gozerbot > /dev/null || true + rm -rf /var/log/gozerbot.log* + ;; + + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + --- gozerbot-0.99.1.orig/debian/compat +++ gozerbot-0.99.1/debian/compat @@ -0,0 +1 @@ +5 --- gozerbot-0.99.1.orig/debian/gozerbot-stop.1 +++ gozerbot-0.99.1/debian/gozerbot-stop.1 @@ -0,0 +1 @@ +.so man1/gozerbot.1 --- gozerbot-0.99.1.orig/debian/gozerbot-init.1 +++ gozerbot-0.99.1/debian/gozerbot-init.1 @@ -0,0 +1 @@ +.so man1/gozerbot.1 --- gozerbot-0.99.1.orig/debian/gozerbot.init +++ gozerbot-0.99.1/debian/gozerbot.init @@ -0,0 +1,111 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: gozerbot +# Required-Start: $local_fs $remote_fs $network $named +# Required-Stop: $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Gozerbot IRC and Jabber bot +# Description: Gozerbot is an IRC and Jabber bot written in Python. +### END INIT INFO + +NAME=gozerbot +DESC="Gozerbot IRC robot" +DATADIR=/var/lib/gozerbot +PIDFILE=$DATADIR/gozerbot.pid +LOGFILE=/var/log/gozerbot.log + +PID=`cat $PIDFILE` 2>/dev/null + +# Include gozerbot defaults if available +if [ -f /etc/default/gozerbot ] ; then + . /etc/default/gozerbot +fi + +if [ "$RUN" != "yes" ] ; then + echo "$NAME disabled; edit /etc/default/gozerbot" + exit 0 +fi + +if [ -z "$RUNUSER" ] ; then + RUNUSER=gozerbot +fi + +status() +{ + test -n "$PID" && ps auxw | grep -v grep | grep gozerbot | grep -q -s " $PID " && return 0 + return 1 +} + +stop() +{ + if status + then + kill -KILL $PID + fi +} + +start() +{ + status && exit 1 # already running + if [ -e $DATADIR ] + then + cd $DATADIR + else + mkdir -p $DATADIR + cd $DATADIR + fi + if [ -e gozerdata/mainconfig ] + then + su $RUNUSER -c "$NAME >> /var/log/gozerbot.log 2>&1 &" + else + su $RUNUSER -c gozerbot-init + rm -rf /etc/gozerbot + ln -s /var/lib/gozerbot/gozerdata /etc/gozerbot + echo "" + echo "A default config file /etc/gozerbot/mainconfig has been created." + echo "irc and jabber bot example files are installed in /etc/gozerbot/fleet." + echo "Please edit those files and then run this script again to start gozerbot." + fi +} + +case "$1" in +start) + echo -n "Starting $DESC: $NAME" + start + case "$?" in + 0) echo "." ; exit 0 ;; + 1) echo " (already running)." ; exit 0 ;; + *) echo " (failed)." ; exit 1 ;; + esac + ;; +stop) + echo -n "Stopping $DESC: $NAME" + stop + case "$?" in + 0) echo "." ; exit 0 ;; + 1) echo " (not running)." ; exit 0 ;; + *) echo " (failed)." ; exit 1 ;; + esac + ;; +restart|force-reload|reload) + echo -n "Restarting $DESC: $NAME" + stop + start + ;; +status) + echo -n "Status of $DESC service: " + status + case "$?" in + 0) echo "running." ; exit 0 ;; + 1) echo "not running." ; exit 3 ;; + esac + ;; +*) + echo "Usage: /etc/init.d/gozerbot {start|stop|reload|force-reload|restart|status}" >&2 + exit 1 + ;; +esac + +*t --- gozerbot-0.99.1.orig/debian/changelog +++ gozerbot-0.99.1/debian/changelog @@ -0,0 +1,132 @@ +gozerbot (0.99.1-3) unstable; urgency=low + + * Remove python-central bits (Closes: #716688). + + -- Jeremy Malcolm Thu, 11 Jul 2013 14:33:31 +0800 + +gozerbot (0.99.1-2) unstable; urgency=low + + * Removes logfiles on purge (Closes: #668767) + * Reverted location of installed files back to /usr/lib/gozerbot. + + -- Jeremy Malcolm Tue, 03 Apr 2012 21:58:28 +0800 + +gozerbot (0.99.1-1) unstable; urgency=low + + * New upstream version (Closes: #630359, #640850) + * Fixed initscript to better test for RUNUSER emptyness (Closes: #612434) + + -- Jeremy Malcolm Thu, 15 Oct 2011 11:54:00 +0800 + +gozerbot (0.9.1.3-6) unstable; urgency=low + + * No longer require an obsolete version of python (Closes: #602006) + + -- Jeremy Malcolm Thu, 19 May 2011 03:45:38 +0800 + +gozerbot (0.9.1.3-5) unstable; urgency=low + + * Added python2.5 build dependency. + + -- Jeremy Malcolm Wed, 29 Sep 2010 18:20:02 +0800 + +gozerbot (0.9.1.3-4) unstable; urgency=low + + * Moved home directory from /var/run to /var/lib (Closes: #571566) + * Corrected dependency on python-sqlalchemy (Closes: #571567) + + -- Jeremy Malcolm Sat, 27 Feb 2010 10:44:53 +0800 + +gozerbot (0.9.1.3-3) unstable; urgency=low + + * Changed dependency on python-setuptools to python-pkg-resources + (Closes: #546435) + + -- Jeremy Malcolm Mon, 14 Sep 2009 09:00:29 +0800 + +gozerbot (0.9.1.3-2) unstable; urgency=low + + * Re-uploaded as a non-native package. + + -- Jeremy Malcolm Fri, 04 Sep 2009 11:53:07 +0800 + +gozerbot (0.9.1.3-1) unstable; urgency=low + + * New upstream release. + + -- Jeremy Malcolm Sat, 15 Aug 2009 15:04:11 +0800 + +gozerbot (0.9.1.2-1) unstable; urgency=low + + * New upstream release (Closes: #516244, #521612, #530405, #523731, + #521611, #521610). + * Split gozerbot-plugins into a separate package. + + -- Jeremy Malcolm Mon, 20 Jul 2009 16:26:52 +0800 + +gozerbot (0.8.1-1) unstable; urgency=low + + * New upstream release. + + -- Jeremy Malcolm Mon, 02 Jun 2008 19:26:39 +0800 + +gozerbot (0.8-1) unstable; urgency=low + + * Upgrades python-xmpp to a dependency (Closes: #480273, #480267) + + -- Jeremy Malcolm Sat, 10 May 2008 16:22:46 +0800 + +gozerbot (0.8) unstable; urgency=low + + * New upstream release + + -- Jeremy Malcolm Wed, 05 Mar 2008 08:55:39 +0900 + +gozerbot (0.7.1.1-1) unstable; urgency=low + + * New upstream release. + + -- Jeremy Malcolm Tue, 25 Sep 2007 14:45:03 +0800 + +gozerbot (0.7.1-1) unstable; urgency=low + + * New upstream release. + + -- Jeremy Malcolm Mon, 20 Aug 2007 21:08:20 +0800 + +gozerbot (0.7-2) unstable; urgency=low + + * New upstream release. + + -- Jeremy Malcolm Wed, 4 Jul 2007 08:26:29 +0800 + +gozerbot (0.6.4.2-2) unstable; urgency=low + + * Worked around upstream bug whereby Python install script no longer + works. + + -- Jeremy Malcolm Thu, 17 May 2007 21:41:26 +0800 + +gozerbot (0.6.4.2-1) unstable; urgency=low + + * New upstream release which fixes build bug. (Closes: #424363) + + -- Jeremy Malcolm Wed, 16 May 2007 14:59:42 +0800 + +gozerbot (0.6.4-3) unstable; urgency=low + + * Added build dependency for python-support. (Closes: #423769) + + -- Jeremy Malcolm Mon, 14 May 2007 21:16:34 +0800 + +gozerbot (0.6.4-2) unstable; urgency=low + + * Corrected minor packaging errors. + + -- Jeremy Malcolm Mon, 30 Apr 2007 16:51:27 +0800 + +gozerbot (0.6.4-1) unstable; urgency=low + + * Initial release. (Closes: #411833) + + -- Jeremy Malcolm Wed, 11 Apr 2007 10:43:10 +0800 --- gozerbot-0.99.1.orig/debian/control +++ gozerbot-0.99.1/debian/control @@ -0,0 +1,24 @@ +Source: gozerbot +Section: net +Priority: optional +Maintainer: Jeremy Malcolm +Build-Depends: python, python-setuptools (>= 0.6b3), debhelper (>= 7), python-support (>= 0.5.3), quilt +Standards-Version: 3.9.1 +XS-Python-Version: all + +Package: gozerbot +Architecture: all +Depends: ${python:Depends}, python-tz (>= 1.0), python-simplejson (>= 1.0), python-feedparser (>= 1.0), python-dns (>= 2.3.3), adduser, python-pkg-resources (>= 0.6b3) +Replaces: gozerbot-plugins +Suggests: gnupg +XB-Python-Version: ${python:Versions} +Provides: ${python:Provides} +Description: IRC and Jabber bot written in Python + Gozerbot is a channel bot that aids with conversation in IRC channels + and Jabber conference rooms. It's mainly used to send notifications (RSS, + Nagios, etc.) and to provide custom commands for the channel. More then + just a channel bot Gozerbot aims to provide a platform for the user to + program his own bot and make it into something that's useful. This is done + with a plugin structure that makes it easy to program your own plugins. + But Gozerbot comes with some batteries included, there are now over 100 + plugins already written and ready for use. --- gozerbot-0.99.1.orig/debian/rules +++ gozerbot-0.99.1/debian/rules @@ -0,0 +1,12 @@ +#!/usr/bin/make -f +include /usr/share/quilt/quilt.make + +%: + dh $@ --with quilt + +binary: build + dh binary --until install --with quilt + rm -rf debian/gozerbot/usr/gozernest + chmod +x debian/gozerbot/usr/lib/gozerbot/gozerbot.cron + chmod +x debian/gozerbot/usr/lib/gozerbot/nagios-udp + dh binary --remaining --- gozerbot-0.99.1.orig/debian/gozerbot-upgrade.1 +++ gozerbot-0.99.1/debian/gozerbot-upgrade.1 @@ -0,0 +1 @@ +.so man1/gozerbot.1 --- gozerbot-0.99.1.orig/debian/gozerbot-install.1 +++ gozerbot-0.99.1/debian/gozerbot-install.1 @@ -0,0 +1 @@ +.so man1/gozerbot.1 --- gozerbot-0.99.1.orig/debian/README.Debian +++ gozerbot-0.99.1/debian/README.Debian @@ -0,0 +1,21 @@ +gozerbot for DEBIAN +------------------- +With version 0.9, the configuration file format for gozerbot has +changed significantly. An experimental upgrade tool, gozerbot-upgrade, +is available. No automatic upgrade will be attempted. To attempt an +upgrade manually, move your existing gozerbot configuration directory +and run the upgrade script as follows: + + cd ~ + mv .gozerbot .gozerbot.old + gozerbot-upgrade .gozerbot.old .gozerbot + +It is now also possible to run a systemwide gozerbot. If you want to do +this, edit the file /etc/defaults/gozerbot to change the value of RUN to +"yes", then run "/etc/init.d/gozerbot start" (it will also run on boot). + +Plugins are included in the separate package gozerbot-plugins. You can +also use the included utility gozerbot-install, which downloads and +installs gozerbot modules over the Internet. + +Jeremy Malcolm --- gozerbot-0.99.1.orig/debian/gozerbot.1 +++ gozerbot-0.99.1/debian/gozerbot.1 @@ -0,0 +1,55 @@ +.TH GOZERBOT 1 "21 Feb 07" "Debian GNU/Linux" "gozerbot manual" +.SH NAME +gozerbot \- an IRC and Jabber bot written in Python +.SH SYNOPSIS +.B gozerbot-start [data directory] +.SH "DESCRIPTION" +This manual page documents briefly the +.B gozerbot +program. +.P +This manual page was written for the Debian GNU/Linux distribution +because the original program does not have a manual page. +Instead, it has documentation in text format; see below. +.PP +.B gozerbot +is an IRC and Jabber bot written in Python. +.SH USAGE +A template configuration file ~/.gozerbot/gozerdata/config will be created when running +.B gozerbot-start +for the first time, or can be created in some other directory by running +.B gozerbot-start [data directory]. +Once at least one valid IRC server or Jabber service for gozerbot to connect to has been specified in that file, running +.B gozerbot-start [data directory] +again will start gozerbot. +.PP +To perform further configuration, message your gozerbot on the IRC server or Jabber service to which it has connected, using the name specified in the configuration file (by default, gozerbot). To summon gozerbot to an IRC channel, you will need to +.B +/msg gozerbot join #channel. +Other useful commands are: + +.TP +.B !perms +.IP +See what permissions you have + +.TP +.B !available +.IP +See what plugins are available + +.TP +.B !commands +.IP +See what commands are available + +.TP +.B !help [command] +.IP +Obtain usage information for a command + +.SH "SEE ALSO" +The program is documented fully in the text documentation. +.SH AUTHOR +This manual page was written by Jeremy Malcolm , +for the Debian GNU/Linux system (but may be used by others). --- gozerbot-0.99.1.orig/debian/README.source +++ gozerbot-0.99.1/debian/README.source @@ -0,0 +1,3 @@ +This package uses quilt to manage all modifications to the upstream source. +Changes are stored in the source package as diffs in debian/patches and +applied during the build. --- gozerbot-0.99.1.orig/debian/gozerbot-start.1 +++ gozerbot-0.99.1/debian/gozerbot-start.1 @@ -0,0 +1 @@ +.so man1/gozerbot.1 --- gozerbot-0.99.1.orig/debian/gozerbot-udp.1 +++ gozerbot-0.99.1/debian/gozerbot-udp.1 @@ -0,0 +1 @@ +.so man1/gozerbot.1 --- gozerbot-0.99.1.orig/debian/manpages +++ gozerbot-0.99.1/debian/manpages @@ -0,0 +1,7 @@ +debian/gozerbot.1 +debian/gozerbot-init.1 +debian/gozerbot-install.1 +debian/gozerbot-start.1 +debian/gozerbot-stop.1 +debian/gozerbot-udp.1 +debian/gozerbot-upgrade.1 --- gozerbot-0.99.1.orig/debian/copyright +++ gozerbot-0.99.1/debian/copyright @@ -0,0 +1,38 @@ +This is gozerbot, written and maintained by Bart Thate + +This package was debianized by Jeremy Malcolm . + +The original source can always be found at: + http://code.google.com/p/gozerbot + +Copyright Holder: B.H.J Thate + +License: + +The individual files in this package have their own copyright either +public domain or BSD. This license is for the software as a whole. + +* Copyright (c) 2005-2007, B.H.J Thate +* 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 nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY B.H.J. Thate ``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 B.H.J. Thate 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. --- gozerbot-0.99.1.orig/debian/gozerbot.default +++ gozerbot-0.99.1/debian/gozerbot.default @@ -0,0 +1,7 @@ +# Defaults for the gozerbot initscript + +# Set to yes to start a system-wide instance of gozerbot +RUN=no + +# Set to the user gozerbot should run as +RUNUSER=gozerbot --- gozerbot-0.99.1.orig/debian/gozerbot.dirs +++ gozerbot-0.99.1/debian/gozerbot.dirs @@ -0,0 +1,2 @@ +usr/lib/gozerbot +usr/share/gozerbot --- gozerbot-0.99.1.orig/debian/dirs +++ gozerbot-0.99.1/debian/dirs @@ -0,0 +1,2 @@ +etc/gozerbot +var/run/gozerbot --- gozerbot-0.99.1.orig/debian/gozerbot.postinst +++ gozerbot-0.99.1/debian/gozerbot.postinst @@ -0,0 +1,59 @@ +#!/bin/sh +# postinst script for gozerbot +# +# see: dh_installdeb(1) +. /usr/share/debconf/confmodule + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + # If gozerbot was previously installed with home directory /var/run/gozerbot + if [ -d /var/run/gozerbot ]; then + mv /var/run/gozerbot /var/lib/gozerbot 2>/dev/null + deluser gozerbot + fi + # If gozerbot was never installed before, or after home directory moved above + if ! getent passwd gozerbot >/dev/null; then + if ! [ -d /var/lib/gozerbot ]; then + mkdir /var/lib/gozerbot 2>/dev/null + fi + adduser --disabled-password --quiet --system \ + --home /var/lib/gozerbot --no-create-home \ + --shell /bin/sh --gecos "Gozerbot" --group gozerbot + touch /var/log/gozerbot.log + chown gozerbot:gozerbot /var/lib/gozerbot + chown gozerbot:gozerbot /var/log/gozerbot.log + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + --- gozerbot-0.99.1.orig/debian/patches/series +++ gozerbot-0.99.1/debian/patches/series @@ -0,0 +1 @@ +setup --- gozerbot-0.99.1.orig/debian/patches/setup +++ gozerbot-0.99.1/debian/patches/setup @@ -0,0 +1,12 @@ +Index: gozerbot-0.99.1/setup.py +=================================================================== +--- gozerbot-0.99.1.orig/setup.py 2012-04-21 17:15:16.000000000 +0800 ++++ gozerbot-0.99.1/setup.py 2012-04-21 17:16:05.000000000 +0800 +@@ -88,6 +88,6 @@ + 'Topic :: Communications :: Chat :: Internet Relay Chat', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], +- data_files=[('files', uploadfiles('files')), ++ data_files=[('lib/gozerbot', uploadfiles('files')), + ('gozernest', uploadfiles('gozernest'))], + ) --- gozerbot-0.99.1.orig/gozerbot.egg-info/SOURCES.txt +++ gozerbot-0.99.1/gozerbot.egg-info/SOURCES.txt @@ -1,6 +1,7 @@ Changelog MANIFEST.in README.txt +setup.cfg setup.py bin/gozerbot bin/gozerbot-init --- gozerbot-0.99.1.orig/gozerbot.egg-info/PKG-INFO +++ gozerbot-0.99.1/gozerbot.egg-info/PKG-INFO @@ -1,4 +1,4 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: gozerbot Version: 0.99.1 Summary: the irc bot and jabber bot in one --- gozerbot-0.99.1.orig/build/lib/gozerbot/cache.py +++ gozerbot-0.99.1/build/lib/gozerbot/cache.py @@ -0,0 +1,12 @@ +# gozerbot.cache +# +# + +""" attempt to make a global usercache. not used yet. """ + +# INIT SECTION + +# global userhost cache +userhosts = {} + +# END SECTION \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/monitor.py +++ gozerbot-0.99.1/build/lib/gozerbot/monitor.py @@ -0,0 +1,168 @@ +# gozerbot/monitor.py +# +# + +""" `gozerbot.monitor` .. monitor the bots output + +This module contains the Monitor base class use to implement callbacks for +the bot's output. Used in logging plugins. The actual monitor objects live +irc and xmpp submodules. + +""" + +__copyright__ = 'this file is in the public domain' + +## IMPORT SECTION + +# gozerbot imports +from gozerbot.config import config +from gozerbot.stats import stats +from utils.log import rlog +from utils.exception import handle_exception +from utils.trace import calledfrom +from config import config +from threads.threadloop import ThreadLoop +from runner import waitrunners +import threads.thr as thr + +# basic imports +import Queue, sys + +## END IMPORT + +## LOCK SECTION + +# no locks + +## END LOCK + +class Monitor(ThreadLoop): + + """ monitor base class. used as base class for jabber and irc + output monitoting. + + :param name: name of the monitor + :type name: string + + """ + + def __init__(self, name="monitor"): + ThreadLoop.__init__(self, name) + self.outs = [] + + def add(self, name, callback, pre, threaded=False): + + """ add a monitoring callback. + + :param name: name of the plugin using this monitor callback + :type name: string + :param callback: the callback to fire + :type callback: function + :param pre: precondition (function) to check if callback should fire + :type pre: function + :param threaded: whether callback should be called in its own thread + :type threaded: boolean + :rtype: boolean + + .. literalinclude:: ../../gozerbot/monitor.py + :pyobject: Monitor.add + + """ + + name = name or calledfrom(sys._getframe(0)) + + if config['loadlist'] and name not in config['loadlist']: + return False + + self.outs.append([name, callback, pre, threaded, False]) + rlog(0, self.name, 'added monitor %s (%s)' % (name, str(callback))) + return True + + def disable(self, name): + name = name.lower() + + for i in range(len(self.outs)-1, -1, -1): + if self.outs[i][0] == name: + self.outs[i][4] = False + + def activate(self, name): + name = name.lower() + + for i in range(len(self.outs)-1, -1, -1): + if self.outs[i][0] == name: + self.outs[i][4] = True + + def unload(self, name): + + """ delete monitor callback. + + :param name: name of the plugin which monitors need to be unloaded + :type name: string + :rtype: integer .. number of monitors removed + + .. literalinclude:: ../../gozerbot/monitor.py + :pyobject: Monitor.unload + + """ + + name = name.lower() + nr = 0 + + for i in range(len(self.outs)-1, -1, -1): + if self.outs[i][0] == name: + del self.outs[i] + nr += 1 + + return nr + + def handle(self, *args, **kwargs): + + """ check if monitor callbacks need to be fired. + + :param args: arguments passed to the callback + :type args: list + :param kwargs: quoted arguments passed to the callback + :type kwargs: dict + :rtype: number of callbacks called + + .. literalinclude:: ../../gozerbot/monitor.py + :pyobject: Monitor.handle + + """ + + nr = 0 + for i in self.outs: + + if not i[4]: + continue + # check if precondition is met + try: + if i[2]: + stats.up('monitors', thr.getname(str(i[2]))) + rlog(-10, 'monitor', 'checking inloop %s' % str(i[2])) + doit = i[2](*args, **kwargs) + else: + doit = 1 + + except Exception, ex: + handle_exception() + doit = 0 + + if doit: + # run monitor callback in its own thread + rlog(0, 'monitor', 'excecuting monitor callback %s' % i[0]) + stats.up('monitors', thr.getname(str(i[1]))) + if not i[3]: + waitrunners.put("monitor-%s" % i[0], i[1], *args) + else: + thr.start_new_thread(i[1], args, kwargs) + + nr += 1 + + return nr + +## INIT SECTION + +# no vars + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/less.py +++ gozerbot-0.99.1/build/lib/gozerbot/less.py @@ -0,0 +1,159 @@ +# gozerbot/less.py +# +# + +""" maintain bot output cache. """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports +from utils.limlist import Limlist + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +class Less(object): + + """ + output cache .. caches upto item of txt lines per nick. + + :param nr: size of backlog + :type nr: integer + + """ + + def __init__(self, nr): + self.data = {} + self.index = {} + self.nr = nr + + def add(self, nick, listoftxt): + + """ + add listoftxt to nick's output .. set index for used by more + commands. + + :param nick: nick to add txt to cache for + :type nick: string + :param listoftxt: list of txt to cache + :type listoftxt: list + + .. literalinclude:: ../../gozerbot/less.py + :pyobject: Less.add + + """ + + nick = nick.lower() + + # see if we already have cached output .. if not create limited list + if not self.data.has_key(nick): + self.data[nick] = Limlist(self.nr) + + # add data + self.data[nick].insert(0, listoftxt) + self.index[nick] = 1 + + def get(self, nick, index1, index2): + + """ + return less entry. + + entry is self.data[nick][index1][index2] + + :param nick: nick to get data for + :type nick: string + :param index1: number of txtlines back + :type index1: integer + :param index2: index into the txtlines + :type index2: integer + :rtype: string + + .. literalinclude:: ../../gozerbot/less.py + :pyobject: Less.get + + """ + + nick = nick.lower() + + try: + txt = self.data[nick][index1][index2] + except (KeyError, IndexError): + txt = None + return txt + + def more(self, nick, index1): + + """ + return more entry pointed to by index .. increase index. + + :param nick: nick to fetch data for + :type nick: string + :param index1: index into cache data + :type index1: integer + :rtype: tuple .. (txt, index) + + .. literalinclude:: ../../gozerbot/less.py + :pyobject: Less.more + + """ + + nick = nick.lower() + + try: + nr = self.index[nick] + except KeyError: + nr = 1 + + try: + txt = self.data[nick][index1][nr] + size = len(self.data[nick][index1])-nr + self.index[nick] = nr+1 + except (KeyError, IndexError): + txt = None + size = 0 + + return (txt, size-1) + + def size(self, nick): + + """ + return sizes of cached output. + + :param nick: nick to get cache sizes for + :type nick: string + :rtype: list .. list of sizes + + .. literalinclude:: ../../gozerbot/less.py + :pyobject: Less.size + + """ + + nick = nick.lower() + sizes = [] + + if not self.data.has_key(nick): + return sizes + + for i in self.data[nick]: + sizes.append(len(i)) + + return sizes + +# ============ +# INIT SECTION + +# no vars + +# END INIT +# ======== \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/partyline.py +++ gozerbot-0.99.1/build/lib/gozerbot/partyline.py @@ -0,0 +1,365 @@ +# gozerbot/partyline.py +# +# + +""" provide partyline functionality .. manage dcc sockets. """ + + +__copyright__ = 'this file is in the public domain' +__author__ = 'Aim' + +## IMPORT SECTION + +# gozerbot imports +from utils.log import rlog +from utils.exception import handle_exception +from fleet import fleet +from simplejson import load +from threads.thr import start_new_thread + +# basic imports +import thread, pickle, socket + +## END IMPORT + +## LOCK SECTION + +# no locks + +## END LOCK + +class PartyLine(object): + + """ + partyline can be used to talk through dcc chat connections. + + """ + + def __init__(self): + self.socks = [] # partyline sockets list + self.jids = [] + self.lock = thread.allocate_lock() + + def _doresume(self, data, reto=None): + + """ + resume a party line connection after reboot. + + :param data: resume data + :type data: dict .. see PartyLine._resumedata + :param reto: nick of user to reply to + :type reto: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject PartyLine._doresume + + """ + + for i in data['partyline']: + bot = fleet.byname(i['botname']) + sock = socket.fromfd(i['fileno'], socket.AF_INET, socket.SOCK_STREAM) + sock.setblocking(1) + nick = i['nick'] + userhost = i['userhost'] + channel = i['channel'] + + if not bot: + rlog(10, 'partyline', "can't find %s bot in fleet" % i['botname']) + continue + + self.socks.append({'bot': bot, 'sock': sock, 'nick': nick, 'userhost': userhost, 'channel': channel, 'silent': i['silent']}) + bot._dccresume(sock, nick, userhost, channel) + + if reto: + self.say_nick(nick, 'rebooting done') + + def _resumedata(self): + + """ + return data used for resume. + + :rtype: list .. list of resumedata (dicts) + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine._resumedata + """ + + result = [] + + for i in self.socks: + result.append({'botname': i['bot'].name, 'fileno': i['sock'].fileno(), 'nick': i['nick'], 'userhost': i['userhost'], 'channel': i['channel'], 'silent': i['silent']}) + + return result + + def resume(self, sessionfile): + + """ + resume from session file. + + :param sessionfile: path to resume file + :type sessionfile: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject PartyLine.resume + """ + + session = load(open(sessionfile, 'r')) + + try: + reto = session['channel'] + self._doresume(session, reto) + + except Exception, ex: + handle_exception() + + def stop(self, bot): + + """ + stop all users on bot. + + :param bot: bot to stop users on + :type bot: gozerbot.eventbase.EventBase + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.stop + """ + + for i in self.socks: + + if i['bot'] == bot: + try: + i['sock'].shutdown(2) + i['sock'].close() + except: + pass + + def stop_all(self): + + """ + stop every user on partyline. + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.stop_all + + """ + + for i in self.socks: + try: + i['sock'].shutdown(2) + i['sock'].close() + except: + pass + + def loud(self, nick): + + """ + enable broadcasting of txt for nick. + + :param nick: nick to put into loud mode + :type nick: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.loud + + """ + + for i in self.socks: + + if i['nick'] == nick: + i['silent'] = False + + def silent(self, nick): + + """ + disable broadcasting txt from/to nick. + + :param nick: nick to put into silent mode + :type nick: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.disable + + """ + + for i in self.socks: + + if i['nick'] == nick: + i['silent'] = True + + def add_party(self, bot, sock, nick, userhost, channel): + + ''' + add a socket with nick to the list. + + :param bot: bot to add party on + :type bot: gozerbot.botbase.BotBase + :param sock: socket of party to add + :type sock: socket.socket + :param nick: nick of party to add + :type nick: string + :param userhost: userhost of party to add + :type userhost: string + :param channel: channel of party to add + :type channel: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.add_party + + ''' + + for i in self.socks: + + if i['sock'] == sock: + return + + self.socks.append({'bot': bot, 'sock': sock, 'nick': nick, \ +'userhost': userhost, 'channel': channel, 'silent': False}) + + rlog(1, 'partyline', 'added user %s on the partyline' % nick) + + def del_party(self, nick): + + ''' + remove a socket with nick from the list. + + :param nick: nick to remove + :type nick: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.del_party + + ''' + + nick = nick.lower() + self.lock.acquire() + + try: + + for socknr in range(len(self.socks)-1, -1, -1): + + if self.socks[socknr]['nick'].lower() == nick: + del self.socks[socknr] + + rlog(1, 'partyline', 'removed user %s from the partyline' % nick) + + finally: + self.lock.release() + + def list_nicks(self): + + ''' + list all connected nicks. + + :rtype: list + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.list_nicks + + ''' + + result = [] + + for item in self.socks: + result.append(item['nick']) + + return result + + def say_broadcast(self, txt): + + ''' + broadcast a message to all ppl on partyline. + + :param txt: txt to broadcast + :type txt: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.say_broadcast + + ''' + + for item in self.socks: + + if not item['silent']: + item['sock'].send("%s\n" % txt) + + def say_broadcast_notself(self, nick, txt): + + ''' + broadcast a message to all ppl on partyline, except the sender. + + :param nick: nick to ignore + :type nick: string + :param txt: text to broadcast + :type txt: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.say_broadcast_notself + + ''' + + nick = nick.lower() + + for item in self.socks: + + if item['nick'] == nick: + continue + + if not item['silent']: + item['sock'].send("%s\n" % txt) + + def say_nick(self, nickto, msg): + + ''' + say a message on the partyline to an user. + + :param nickto: nick to send txt to + :type nickto: string + :param msg: msg to send + :type msg: string + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.say_nick + + ''' + + nickto = nickto.lower() + + for item in self.socks: + + if item['nick'].lower() == nickto: + + if not '\n' in msg: + msg += "\n" + + item['sock'].send("%s" % msg) + return + + def is_on(self, nick): + + ''' + checks if user an is on the partyline. + + :param nick: nick to check + :type nick: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/partyline.py + :pyobject: PartyLine.is_on + + ''' + + nick = nick.lower() + + for item in self.socks: + + if item['nick'].lower() == nick: + return True + + return False + +## INIT SECTION + +# the partyline ! +partyline = PartyLine() + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/plughelp.py +++ gozerbot-0.99.1/build/lib/gozerbot/plughelp.py @@ -0,0 +1,73 @@ +# gozerbot/plughelp.py +# +# + +""" + help about plugins. + +""" + +__copyright__ = 'this file is in the public domain' + +## IMPORT SECTION + +# no imports + +## END IMPORT + +## LOCK SECTION + +# no locks + +## END LOCK + +class PlugHelp(dict): + + """ + dict holding plugins help string. + + """ + + def add(self, item, descr): + + """ + add plugin help string. + + :param item: the plugin to store description for + :type item: string + :param descr: the description of the plugin + :type descr: string + + .. literalinclude ../../gozerbot/plughelp.py + :pyobject: PlugHelp.add + + """ + + item = item.lower() + self[item] = descr + + def get(self, item): + + """ + get plugin help string. + + :param item: plugin to get description for + :type item: string + :rtype: string + + .. literalinclude:: ../../gozerbot/plughelp.py + :pyobject: PlugHelp.get + """ + + item = item.lower() + try: + return self[item] + except KeyError: + return None + +## INIT SECTION + +# plughelp object +plughelp = PlugHelp() + +# END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/generic.py +++ gozerbot-0.99.1/build/lib/gozerbot/generic.py @@ -0,0 +1,31 @@ +# generic compat stub +# +# + +""" utils stub for backwards compatibility. """ + +# ============== +# IMPORT SECTION + +from utils.log import * +from utils.generic import * +from utils.exception import * +from utils.popen import * +from utils.timeutils import * +from utils.fileutils import * +from utils.reboot import * +from utils.trace import * +from utils.locking import * +from utils.rsslist import * +from utils.url import * + +# END IMPORT +# ========== + +# ========= +# LOCK SECTION + +# no locks + +# END LOCK +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugins.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugins.py @@ -0,0 +1,1509 @@ +# gozerbot/plugins.py +# +# + +""" provide plugin infrastructure """ + +__copyright__ = 'this file is in the public domain' + +## gozerbot imports + +from gozerbot.stats import stats +from gozerbot.tests import tests +from gozerbot.datadir import datadir +from users import users +from irc.monitor import outmonitor, saymonitor +from xmpp.monitor import xmppmonitor +from utils.log import rlog +from utils.exception import handle_exception +from utils.generic import checkchan +from utils.locking import lockdec, funclocked, Locked +from utils.generic import plugnames, waitforqueue, uniqlist, makeoptions, makeargrest, cleanpycfile +from gozerimport import gozer_import, force_import +from persist.persist import Persist +from persist.persistconfig import PersistConfig +from config import config +from commands import cmnds +from callbacks import callbacks, jcallbacks, gn_callbacks +from redispatcher import rebefore, reafter +from aliases import aliascheck, aliasget +from ignore import shouldignore +from threads.thr import start_new_thread, getname +from persist.persiststate import PersistState +from simplejson import loads +from morphs import inputmorphs, outputmorphs +from eventbase import EventBase +from admin import cmndtable, pluginlist + +# basic imports +import os, os.path, thread, time, Queue, re, copy + +## END IMPORT + +## LOCK SECTION + +loadlock = thread.allocate_lock() +loadlocked = lockdec(loadlock) + +## END LOCK + +class Plugins(object): + + """ + hold all the plugins. + + """ + + def __init__(self): + self.plugs = {} # dict with the plugins + self.reloadlock = thread.allocate_lock() + # persisted data for deny of plugins (blacklist) + self.plugdeny = Persist(datadir + os.sep + 'plugdeny', init=False) + if not self.plugdeny.data: + self.plugdeny.data = [] + # persisted data for allowing of plugins (whitelist) + self.plugallow = Persist(datadir + os.sep + 'plugallow', init=False) + if not self.plugallow.data: + self.plugallow.data = [] + self.avail = [] # available plugins + self.ondisk = [] # plugisn available for reload + self.initcalled = [] # plugins which init functions are called + self.overloads = {} # plugins to overload + self.activated = {} + for plug in config['plugdeny']: + self.disable(plug) + + def __getitem__(self, item): + + """ + return plugin. + + """ + + if self.plugs.has_key(item): + return self.plugs[item] + else: + return None + + def get(self, item, attr): + + """ + get attribute of plugin. + + :param item: plugin to get attribute of + :type item: string + :param attr: attribute to fetch + :type attr: string + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.get + + """ + + if self.plugs.has_key(item): + return getattr(self.plugs[item], attr) + + def whatperms(self): + + """ + return what permissions are possible. + + :rtype: list + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.whatperms + + """ + + result = [] + + # search RE callbacks before the commands + for i in rebefore.whatperms(): + if not i in result: + result.append(i) + + # search the commands + for i in cmnds.whatperms(): + if not i in result: + result.append(i) + + # search RE callbacks after commands + for i in reafter.whatperms(): + if not i in result: + result.append(i) + + result.sort() + return result + + def exist(self, name): + + """ + see if plugin exists. + + :param name: name of plugin to check for + :type name: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.exist + + """ + + if self.plugs.has_key(name): + return True + return False + + def disable(self, name): + + """ + prevent plugin to be loaded. plugins does get imported but + commands, callbacks, monitors etc are not enabled. + + :param name: name of the plugin to disable + :type name: string + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.disable + + """ + + try: + config['loadlist'].remove(name) + config.save() + self.plugdeny.data.append(name) + self.plugdeny.save() + + except: + pass + + def enable(self, name): + + """ + enable plugin. + + :param name: name of plugin to enable + :type name: string + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.enable + + """ + + try: + + if name not in config['loadlist']: + config['loadlist'].append(name) + config.save() + + except KeyError: + pass + + try: + self.plugdeny.data.remove(name) + self.plugdeny.save() + + except ValueError: + pass + + def plugsizes(self): + + """ + call the size() function in all plugins. + + :rtype: list .. list of plugin sizes + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.plugsizes + """ + + reslist = [] + cp = dict(self.plugs) + for i, j in cp.iteritems(): + try: + reslist.append("%s: %s" % (i, j.size())) + except AttributeError: + pass + return reslist + + def list(self): + + """ + list of registered plugins. + + :rtype: list .. list of enabled plugins + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.list + + """ + + self.avail.sort() + return self.avail + + def plugimport(self, mod, name): + + """ + import a plugin. + + :param mod: module to import plugin from + :type mod: string + :param name: name of the plugin to import + :type name: string + :rtype: module .. the plugin + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.plugimport + + """ + + if name in config['loadlist']: + return self.load(mod, name) + + def regplugin(self, mod, name): + + """ + register plugin. + + :param mod: module to import plugin from + :type mod: string + :param name: name of the plugin to import + :type name: string + :rtype: module .. the plugin + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.regplugin + + """ + + name = name.lower() + mod = mod.lower() + modname = mod + '.' + name + + # see if plugin is in deny + if name in self.avail: + rlog(0, 'plugins', '%s already registered' % name) + return + + if name in config['plugdeny']: + rlog(0, 'plugins', '%s is in config.plugdeny .. not loading' % name) + return + + if name in self.plugdeny.data: + rlog(0, 'plugins', '%s.%s in deny .. not loading' % (mod, name)) + return 0 + + if config.has_key('loadlist') and name not in config['loadlist'] and 'gplugs' in modname and name not in self.plugallow.data: + rlog(9, 'plugins', 'not loading %s.%s' % (mod, name)) + return 0 + + # if plugin is already registered unload it + if self.plugs.has_key(name): + rlog(10, 'plugins', 'overloading %s plugin with %s version' % (name, mod)) + self.unloadnosave(name) + + # create the plugin data dir + if hasattr(os, 'mkdir'): + if not os.path.isdir(datadir + os.sep + 'plugs'): + os.mkdir(datadir + os.sep + 'plugs') + + if not os.path.isdir(datadir + os.sep + 'plugs' + os.sep + name): + os.mkdir(datadir + os.sep + 'plugs' + os.sep + name) + + # import the plugin + plug = self.plugimport(mod, name) + + if plug: + rlog(0, 'plugins', "%s.%s registered" % (mod, name)) + + if name not in self.avail: + self.avail.append(name) + + return plug + + else: + rlog(10, 'plugins', "can't import %s.%s .. try plug-enable" % (mod, name)) + + def showregistered(self): + + """ + show registered plugins. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.showregistered + + """ + + self.avail.sort() + rlog(10, 'plugins', 'registered %s' % ' .. '.join(self.avail)) + self.overload() + + def regdir(self, dirname, exclude=[]): + + """ + register a directory. + + :param dirname: directory to import plugins from + :type dirname: string + :param exclude: plugins to exclude from importing + :type exclude: list .. list of plugin names + :rtype: list .. list of plugin names that are registered + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.regdir + + """ + + threads = [] + plugs = [] + for plug in plugnames(dirname): + if plug in exclude or plug.startswith('.'): + continue + try: + self.regplugin(dirname, plug) + plugs.append(plug) + except: + handle_exception() + self.ondisk.extend(plugs) + return plugs + + def regcore(self): + + """ + register core plugins. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.regcore + + """ + + self.plugdeny.init([]) + self.plugallow.init([]) + avail = [] + plugs = force_import('gozerbot.plugs') + + for i in plugs.__plugs__: + + if i not in avail: + + try: + self.regplugin('gozerbot.plugs', i) + except Exception, ex: + handle_exception() + else: + avail.append(i) + + self.ondisk.extend(avail) + + def enableall(self): + + """ + enable all plugins + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.enableall + + """ + + for name in self.available(): + self.enable(name) + + def regplugins(self): + + """ + register all plugins. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.regplugins + + """ + + self.regcore() + avail = [] + + # check for myplugs directory + if os.path.isdir('myplugs'): + avail.extend(self.regdir('myplugs')) + + for i in os.listdir('myplugs'): + + if i.startswith('.'): + continue + + if os.path.isdir('myplugs' + os.sep + i): + avail.extend(self.regdir('myplugs' + os.sep + i)) + else: + rlog(10, 'plugins', 'no myplugs directory found') + + # check for gplugs package + try: + gplugs = gozer_import('gplugs') + except ImportError: + rlog(20, 'plugins', "no gplugs package found") + gplugs = None + + if gplugs: + + for i in gplugs.__plugs__: + + try: + self.regplugin('gplugs', i) + avail.append(i) + except Exception, ex: + handle_exception() + + if config.get('db_driver') == "olddb": + # check for gplugs package + try: + gplugs = gozer_import('gplugs.olddb') + except ImportError: + rlog(20, 'plugins', "no gplugs.old package found") + gplugs = None + + if gplugs: + + for i in gplugs.__plugs__: + + try: + self.regplugin('gplugs.olddb', i) + avail.append(i) + except Exception, ex: + handle_exception() + else: + # check for gplugs package + try: + gplugs = gozer_import('gplugs.alchemy') + except ImportError: + rlog(20, 'plugins', "no gplugs.alchemy package found") + gplugs = None + + if gplugs: + + for i in gplugs.__plugs__: + + try: + self.regplugin('gplugs.alchemy', i) + avail.append(i) + except Exception, ex: + handle_exception() + + self.ondisk.extend(avail) + self.readoverload() + start_new_thread(self.showregistered, ()) + + def readoverload(self): + + """ + see if there is a permoverload file and if so use it to overload + permissions based on function name. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.readoverload + + """ + + try: + overloadfile = open(datadir + os.sep + 'permoverload', 'r') + except IOError: + return + + try: + + for i in overloadfile: + i = i.strip() + splitted = i.split(',') + + try: + funcname = splitted[0].strip() + perms = [] + for j in splitted[1:]: + perms.append(j.strip()) + except IndexError: + rlog(10, 'plugins', "permoverload: can't set perms of %s" \ +% i) + continue + + if not funcname: + rlog(10, 'plugins', "permoverload: no function provided") + continue + + if not perms: + rlog(10, 'plugins', "permoverload: no permissions \ +provided for %s" % funcname) + continue + + self.overloads[funcname] = perms + + except Exception, ex: + handle_exception() + + def overload(self): + + """ + overload functions in self.overloads. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.overload + + """ + for funcname, perms in self.overloads.iteritems(): + + if self.permoverload(funcname, perms): + rlog(0, 'plugins', '%s permission set to %s' % (funcname, \ +perms)) + + def available(self): + + """ + available plugins not yet registered. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.available + + """ + + self.ondisk.sort() + return self.ondisk + + def saveplug(self, plugname): + + """ + call save() function of plugin. + + :param plugname: name of the plugin to call save() on + :type plugname: string + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.saveplug + """ + + try: + self.plugs[plugname].save() + + except AttributeError: + pass + + except KeyError: + pass + + def save(self): + + """ + call registered plugins save. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.save + + """ + + for plug in self.plugs.values(): + + try: + plug.save() + + except AttributeError: + pass + + except Exception, ex: + handle_exception() + + def save_cfg(self): + + """ + call registered plugins configuration save. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.save_cfg + + """ + + for plug in self.plugs.values(): + try: + cfg = getattr(plug, 'cfg') + if isinstance(cfg, PersistConfig): + try: + cfg.save() + except: + handle_exception() + except AttributeError: + continue + + def save_cfgname(self, name): + + """ + save plugin persisted config data. + + :param name: name of the plugin to call cfg.save for + :type name: string + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.save_cfgname + """ + + try: + plug = self.plugs[name] + cfg = getattr(plug, 'cfg') + + if isinstance(cfg, PersistConfig): + + try: + cfg.save() + except: + handle_exception() + + except (AttributeError, KeyError): + pass + + def exit(self): + + """ + call shutdown on all registered plugins. + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.exit + + """ + + self.save() + threadlist = [] + + # call shutdown on all plugins + for name, plug in self.plugs.iteritems(): + + try: + shutdown = getattr(plug, 'shutdown') + thread = start_new_thread(shutdown, ()) + threadlist.append((name, thread)) + + try: + self.initcalled.remove(name) + except ValueError: + pass + + except AttributeError: + continue + + except Exception, ex: + rlog(10, 'plugins', 'error shutting down %s: %s' % (name, str(ex))) + + # join shutdown threads + try: + + for name, thread in threadlist: + thread.join() + rlog(10, 'plugins', '%s shutdown finished' % name) + except: + handle_exception() + + def getoptions(self, command): + + """ + return options entry of a command. + + :param command: command name to get options of + :type command: string + :rtype: dict + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.getoptions + + """ + + return cmnds.getoptions(command) + + def getdepend(self, plugname): + + """ + get plugins the plugin depends on. NOT USED ANYMORE .. + + we use the __depending__ attribute now which checks for the + reverse case ala what plugins depends on this plugin. code is + in reload(). + + """ + + # try to import the plugin + if plugname in self.plugs: + plug = self.plugs[plugname] + else: + for mod in ['gozerbot.plugs', 'gplugs', 'myplugs']: + try: + plug = gozer_import('%s.%s' % (mod, plugname)) + except ImportError: + continue + + # check for the __depend__ attribute + try: + depends = plug.__depend__ + except: + depends = [] + + return depends + + def load(self, mod , name, enable=True): + #if name in config['plugdeny']: + # return + # force an import of the plugin + modname = mod + '.' + name + self.down(name) + self.unload(name) + if enable: + self.enable(name) + plug = self.plugs[name] = gozer_import(modname) + plug.loadtime = time.time() + if enable: + self.enable(name) + self.overload() + # call plugins init() function + try: + rlog(0, 'plugins', 'calling %s init()' % modname) + plug.init() + self.initcalled.append(modname) + + except (AttributeError, KeyError): + pass + + except Exception, ex: + rlog(10, 'plugins', '%s module init failed' % name) + raise + + rlog(0, 'plugins', 'enabled %s' % name) + + self.activate(name) + return plug + + #@loadlocked + def reload(self, mod, name, enable=True): + + """ + reload plugin. + + :param mod: module to import plugin from + :type mod: string + :param name: name of the plugin to reload + :type name: string + :param enable: whether plugin should be enabled on reload + :type enable: boolean + :rtype: list .. list of names of reloaded plugins + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.reload + + """ + + # create the plugin data dir + if not os.path.isdir(datadir + os.sep + 'plugs'): + os.mkdir(datadir + os.sep + 'plugs') + + if not os.path.isdir(datadir + os.sep + 'plugs' + os.sep + name): + os.mkdir(datadir + os.sep + 'plugs' + os.sep + name) + + reloaded = [] + modname = mod + '.' + name + + # force an import of the plugin + plug = self.load(mod, name) + + # recurse the reload function if plugin is a dir + try: + for p in plug.__plugs__: + self.load(modname, p) + reloaded.append(p) + + except (KeyError, AttributeError): + pass + + rlog(0, 'plugins', 'reloaded plugin %s' % modname) + reloaded.append(name) + self.plugallow.data.append(name) + + try: + self.plugdeny.data.remove(name) + except ValueError: + pass + + if name not in self.avail: + self.avail.append(name) + + # recurse on plugins the depend on this plugin + try: + depends = plug.__depending__ + + for plug in depends: + rlog(10, 'plugins', 'loading depending plugin %s (%s)' % (plug, name)) + self.load(mod, plug, False) + reloaded.append(plug) + + except AttributeError: + pass + + return reloaded + + def activate(self, plugname): + self.activated[plugname] = True + try: + cmnds.activate(plugname) + callbacks.activate(plugname) + gn_callbacks.activate(plugname) + jcallbacks.activate(plugname) + rebefore.activate(plugname) + reafter.activate(plugname) + saymonitor.activate(plugname) + outmonitor.activate(plugname) + xmppmonitor.activate(plugname) + tests.activate(plugname) + outputmorphs.activate(plugname) + inputmorphs.activate(plugname) + except Exception, ex: + handle_exception() + return 0 + + def down(self, plugname): + self.activated[plugname] = False + try: + cmnds.disable(plugname) + callbacks.disable(plugname) + gn_callbacks.disable(plugname) + jcallbacks.disable(plugname) + rebefore.disable(plugname) + reafter.disable(plugname) + saymonitor.disable(plugname) + outmonitor.disable(plugname) + xmppmonitor.disable(plugname) + tests.disable(plugname) + outputmorphs.disable(plugname) + inputmorphs.disable(plugname) + except Exception, ex: + handle_exception() + return 0 + + def unload(self, plugname): + + """ + unload plugin. + + :param plugname: name of the plugin to unload + :type plugname: string + :rtype: list .. list of unloaded plugins + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.unload + + """ + + # call plugins shutdown function if available + unloaded = [plugname, ] + + # recurse if plugin is dir + try: + plug = self.plugs[plugname] + + for p in plug.__plugs__: + if p == plug: + raise Exception("same plugin name as dir name (%s)" % plugname) + unloaded.extend(self.unload(p)) + + except (KeyError, AttributeError): + pass + + # save and unload + for plugname in unloaded: + self.saveplug(plugname) + self.unloadnosave(plugname) + + try: + self.avail.remove(plugname) + + except ValueError: + pass + + return unloaded + + def unloadnosave(self, plugname): + + """ + unload plugin without saving. + + :param plugname: name of the plugin to unload + :type plugname: string + :rtype: list .. list of unloaded plugins + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.unloadnosave + + """ + + # call shutdown function + try: + self.plugs[plugname].shutdown() + rlog(10, 'plugins', '%s shutdown called' % plugname) + + except (AttributeError, KeyError): + pass + + except Exception, ex: + handle_exception() + + # remove from plugallow + try: + self.plugallow.data.remove(plugname) + except (KeyError, ValueError): + pass + + # remove from avail list + try: + self.avail.remove(plugname) + except ValueError: + pass + + # remove from initcalled list + try: + self.initcalled.remove(plugname) + except ValueError: + pass + + # unload commands, RE callbacks, callbacks, monitorsetc. + try: + cmnds.unload(plugname) + callbacks.unload(plugname) + gn_callbacks.unload(plugname) + jcallbacks.unload(plugname) + rebefore.unload(plugname) + reafter.unload(plugname) + saymonitor.unload(plugname) + outmonitor.unload(plugname) + xmppmonitor.unload(plugname) + tests.unload(plugname) + outputmorphs.unload(plugname) + inputmorphs.unload(plugname) + + if self.plugs.has_key(plugname): + del self.plugs[plugname] + + except Exception, ex: + handle_exception() + return 0 + + rlog(0, 'plugins', '%s unloaded' % plugname) + return 1 + + def whereis(self, what): + + """ + locate command. + + :param what: name of command to search + :type what: string + :rtype: string .. plugin the command is in + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.whereis + + """ + + return cmnds.whereis(what) + + def permoverload(self, funcname, perms): + + """ + overload permission of a function. + + :param funcname: name of function to overload permissions of + :type funcname: string + :param perms: permissions to overload + :type perms: list .. list of permissions + :rtype: boolean: whether overload worked or not + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.permoverload + + """ + + if not rebefore.permoverload(funcname, perms): + + if not cmnds.permoverload(funcname, perms): + + if not reafter.permoverload(funcname, perms): + return False + + return True + + def woulddispatch(self, bot, ievent): + + """ + function to determine whether a event would dispatch. + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :rtype: boolean .. whether the dispatch should fire + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.woulddispatch + + """ + #self.needreloadcheck(bot, ievent) + (what, command) = self.dispatchtest(bot, ievent) + + #if what and not what.activate: + # return False + + if what and command: + return True + + return False + + #@funclocked + def dispatchtest(self, bot, ievent, direct=False): + + """ + return (dispatcher, command) on which command should fire. + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :param direct: whether user permission should be checked + :type direct: boolean .. when set user permission is NOT checked + :rtype: tuple .. (dispatcher, command) tuples + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.dispatchtest + """ + + # check for ignore + if shouldignore(ievent.userhost): + return (None, None) + + # check for throttle + if ievent.userhost in bot.throttle: + return (None, None) + + # set target properly + if ievent.txt.find(' | ') != -1: + target = ievent.txt.split(' | ')[0] + elif ievent.txt.find(' && ') != -1: + target = ievent.txt.split(' && ')[0] + else: + target = ievent.txt + + result = [] + + # first check for RE before commands dispatcher + com = rebefore.getcallback(target) + + if com and not target.startswith('!'): + com.re = True + result = [rebefore, com] + else: + + # try commands + if ievent.txt.startswith('!'): + ievent.txt = ievent.txt[1:] + + aliascheck(ievent) + com = cmnds.getcommand(ievent.txt) + + if com: + com.re = False + result = [cmnds, com] + ievent.txt = ievent.txt.strip() + + else: + + # try RE after commands + com = reafter.getcallback(target) + if com: + com.re = True + result = [reafter, com] + if result: + + # check for auto registration + if config['auto_register'] and not users.getname(ievent.userhost): + if bot.google: + users.add(ievent.userhost , [ievent.userhost, ], ['USER', ]) + elif not bot.jabber: + users.add("%s!%s" % (ievent.nick, ievent.userhost) , [ievent.userhost, ], ['USER', ]) + bot.ratelimit(ievent.userhost, 20) + else: + if ievent.groupchat: + users.add(ievent.userhost , [ievent.userhost, ], ['USER', ]) + bot.ratelimit(ievent.userhost) + else: + users.add(ievent.stripped , [ievent.stripped, ], ['USER', ]) + bot.ratelimit(ievent.stripped, 20) + + # check for anon access + if config['anon_enable'] and not 'OPER' in result[1].perms: + return result + + # check if command is allowed (all-add command) + if com.name in bot.state['allowed'] or getname(com.func) in bot.state['allowed']: + return result + + # check for channel permissions + try: + chanperms = bot.channels[ievent.channel.lower()]['perms'] + + for i in result[1].perms: + if i in chanperms and not ievent.msg: + ievent.speed = 1 + return result + + except (KeyError, TypeError): + pass + + # if direct is set dont check the user database + if direct: + return result + + # use event.stripped in case of jabber + if bot.jabber and ievent.jabber: + if not ievent.groupchat or ievent.jidchange: + if users.allowed(ievent.stripped, result[1].perms): + return result + + # irc users check + if users.allowed(ievent.userhost, result[1].perms): + return result + + return (None, None) + + def cmnd(self, bot, ievent, timeout=15, response=False, onlyqueues=True): + + """ + launch command and wait for result. + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :param timeout: number of seconds to wait for a result + :type timeout: integer + :param response: whether to notify user we are running the command + :type response: string + :rtype: list .. list of results + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.cmnd + + """ + if response: + ievent.reply('launching %s on %s bot' % (ievent.txt, bot.name)) + #ii = self.clonedevent(bot, ievent) + #q = Queue.Queue() + #ii.queues.append(q) + #ii.onlyqueues = onlyqueues + q = Queue.Queue() + ievent.queues.append(q) + self.trydispatch(bot, ievent) + return waitforqueue(q, timeout) + + def waitdispatch(self, bot, ievent, direct=False): + + """ + dispatch command and wait for results. + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :param direct: whether user permission should be checked + :type direct: boolean .. when set user permission is NOT checked + :rtype: boolean .. whether dispatch succeeded or not + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.waitdispatch + + """ + ievent.threaded = True + return self.trydispatch(bot, ievent, direct, wait=True) + + def trydispatch(self, bot, ievent, direct=False, wait=False): + + """ + try to dispatch ievent. + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :param direct: whether user permission should be checked + :type direct: boolean .. when set user permission is NOT checked + :param wait: whether function should wait for results + :type wait: boolean + :rtype: boolean .. whether dispatch succeeded or not + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.trydispatch + + """ + + # test for ignore + if shouldignore(ievent.userhost): + return 0 + + # set printto + if ievent.msg: + ievent.printto = ievent.nick + else: + ievent.printto = ievent.channel + + # see if ievent would dispatch + # what is rebefore, cmnds of reafter, com is the command object + # check if redispatcher or commands object needs to be used + #self.needreloadcheck(bot, ievent) + + (what, com) = self.dispatchtest(bot, ievent, direct) + + if what: + + if com.allowqueue: + ievent.txt = ievent.txt.replace(' || ', ' | ') + + if ievent.txt.find(' | ') != -1: + + if ievent.txt[0] == '!': + ievent.txt = ievent.txt[1:] + else: + self.splitpipe(bot, ievent) + return + + elif ievent.txt.find(' && ') != -1: + self.multiple(bot, ievent) + return + + return self.dispatch(what, com, bot, ievent, wait) + + def dispatch(self, what, com, bot, ievent, wait=False): + + """ + do the actual dispatch of event. + + :param what: the dispatcher to dispatch the command on + :type what: gozerbot.redispatcher.REdispatcher or gozerbot.commands.Commands + :param com: the command to dispatch + :type com: gozerbot.redispatcher.REcallback or gozerbot.commands.Command + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :param wait: whether function should wait for results + :type wait: boolean + :rtype: boolean .. whether dispatch succeeded or not + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.dispatch + + """ + if bot.stopped: + return False + + # make command options + if com.options: + makeoptions(ievent, com.options) + else: + makeoptions(ievent) + + # make arguments and rest + makeargrest(ievent) + ievent.usercmnd = True + rlog(10, 'plugins', 'dispatching %s for %s' % (ievent.command, ievent.userhost)) + + # call dispatch + what.dispatch(com, bot, ievent, wait) + return True + + def clonedevent(self, bot, event): + + """ + clone a event. + + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + :rtype: gozerbot.eventbase.EventBase + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.clonedevent + + """ + + ie = copy.deepcopy(event) + return ie + + + def multiple(self, bot, ievent): + + """ + execute multiple commands. + + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.multiple + + """ + + for i in ievent.txt.split(' && '): + ie = self.clonedevent(bot, ievent) + ie.txt = i + #self.needreloadcheck(bot, ievent) + self.trydispatch(bot, ie) + + def splitpipe(self, bot, ievent): + + """ + execute commands in a pipeline. + + :param bot: bot on which command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering the command + :type ievent: gozerbot.eventbase.EventBase + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.splitpipe + + """ + + origqueues = ievent.queues + ievent.queues = [] + events = [] + txt = ievent.txt.replace(' || ', ' ##') + + # split commands + for i in txt.split(' | '): + item = i.replace(' ##', ' | ') + ie = self.clonedevent(bot, ievent) + ie.userhost = ievent.userhost + ie.onlyqueues = True + ie.txt = item.strip() + #self.needreloadcheck(bot, ie) + events.append(ie) + + # loop over events .. chain queues + prevq = None + + for i in events[:-1]: + q = Queue.Queue() + i.queues.append(q) + if prevq: + i.inqueue = prevq + prevq = q + + events[-1].inqueue = prevq + events[-1].onlyqueues = False + + if origqueues: + events[-1].queues = origqueues + + # check if all commands would dispatch + for i in events: + if not self.woulddispatch(bot, i): + ievent.reply("can't execute %s" % str(i.txt)) + return + + # do the dispatch + for i in events: + (what, com) = self.dispatchtest(bot, i) + if what: + self.dispatch(what, com, bot, i) + + def needreloadcheck(self, bot, event, target=None): + + if cmndtable: + + try: + if target: + rlog(10, 'plugins', 'target set: %s' % target) + cmnd = 'target-set' + plugin = target + else: + t = event.txt.split()[0] + cmnd = aliasget(t) or t + plugin = cmndtable[cmnd] + + rlog(10, 'plugins', 'cmnd: %s plugin: %s' % (cmnd, plugin)) + + if self.exist(plugin): + return + + try: + self.reload('gozerbot.plugs', plugin) + except ImportError, ex: + + try: + self.reload('gplugs', plugin) + except ImportError, ex: + if config.get('db_driver') == 'olddb': + try: + self.reload('gplugs.olddb', plugin) + except ImportError, ex: pass + else: + try: + self.reload('gplugs.alchemy', plugin) + except ImportError, ex: pass + try: + self.reload('myplugs', plugin) + except ImportError, ex: + return + + rlog(100, 'plugins', 'reloaded %s' % plugin) + + except KeyError, ex: + rlog(10, 'plugins', "can't find plugin to reload for %s" % event.txt.split()[0]) + + + def listreload(self, pluglist): + + """ + reload list of plugins. + + :param pluglist: list of plugin names + :type pluglist: list + :rtype: list .. list of plugins where reload failed + + .. literalinclude:: ../../gozerbot/plugins.py + :pyobject: Plugins.listreload + + """ + + failed = [] + + # loop over the plugin list and reload them + for what in pluglist: + splitted = what[:-3].split(os.sep) + mod = '.'.join(splitted[:-1]) + + if not mod: + if config.get('db_driver') == "olddb": + mod = "gplugs.olddb" + elif config.get("db_driver") == "alchemy": + mod = "gplugs.alchemy" + else: + mod = 'gplugs' + + plug = splitted[-1] + + # reload the plugin + try: + self.reload(mod, plug) + + except Exception, ex: + failed.append(what) + + return failed + +## INIT SECTION + +# THE plugins object +plugins = Plugins() + +## END INIT + --- gozerbot-0.99.1.orig/build/lib/gozerbot/users.py +++ gozerbot-0.99.1/build/lib/gozerbot/users.py @@ -0,0 +1,18 @@ +# gozerbot/users.py +# +# + +from gozerbot.config import config + +if config.get('db_driver') == "olddb": + try: + import gozerbot.database.db + import gozerbot.dbusers + except Exception, ex: + handle_exception() + rlog(100, 'users', 'an error has occured while trying to enable the mysql database') + die() + users = gozerbot.dbusers.Dbusers() +else: + import gozerbot.sausers as sa + users = sa.DbUsers() --- gozerbot-0.99.1.orig/build/lib/gozerbot/ignore.py +++ gozerbot-0.99.1/build/lib/gozerbot/ignore.py @@ -0,0 +1,126 @@ +# gozerbot/ignore.py +# +# + +""" ignore module. """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports +from persist.persist import Persist +from datadir import datadir +from periodical import interval + +# basic imports +import time, os, thread + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +def addignore(userhost, ttime): + + """ + add ignore based on userhost .. record time when ignore is set. + + :param userhost: userhost to ignore + :type userhost: string + :param ttime: duration of the ignore + :type ttime: integer + + .. literalinclude:: ../../gozerbot/ignore.py + :pyobject: addignore + + """ + + global ignore + + ignore[userhost] = int(ttime) + timeset[userhost] = time.time() + +def delignore(userhost): + + """ + remove ignore. + + :param userhost: userhost to remove ignore from + :type userhost: string + :rtype: boolean .. whether delete was succesfull + + .. literalinclude:: ../../gozerbot/ignore.py + :pyobject: delignore + + """ + + global ignore + + try: + del ignore[userhost] + del timeset[userhost] + return True + except KeyError: + return False + +def shouldignore(userhost): + + """ + check if we should ignore. + + :param userhost: userhost to check whether we should ignore it + :type userhost: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/ignore.py + :pyobject: shouldignore + + """ + + try: + ignoretime = ignore[userhost] + ignoreset = timeset[userhost] + except KeyError: + return False + + if time.time() - ignoretime < ignoreset: + return True + + return False + + +@interval(60) +def ignorecheck(): + + """ + periodic function to remove users that no longer need to be ignored. + + .. literalinclude:: ../../gozerbot/ignore.py + :pyobject: ignorecheck + + """ + + for userhost in ignore.keys(): + if not shouldignore(userhost): + delignore(userhost) + + +# ============ +# INIT SECTION + +ignore = {} +timeset = {} + +# first call to trigger interval +ignorecheck() + +# END INIT +# ======= \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/wait.py +++ gozerbot-0.99.1/build/lib/gozerbot/wait.py @@ -0,0 +1,168 @@ +# gozerbot/wait.py +# +# + +""" wait for ircevent based on ircevent.CMND """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from utils.log import rlog +from utils.locking import lockdec +import threads.thr as thr + +# basic imports +import time, thread + +# locks +waitlock = thread.allocate_lock() +locked = lockdec(waitlock) + +class Wait(object): + + """ lists of ircevents to wait for """ + + def __init__(self): + self.waitlist = [] + self.ticket = 0 + + def register(self, cmnd, catch, queue, timeout=15): + + """ register wait for cmnd. """ + + rlog(1, 'wait', 'registering for cmnd ' + cmnd) + self.ticket += 1 + self.waitlist.insert(0, (cmnd, catch, queue, self.ticket)) + if timeout: + # start timeout thread + thr.start_new_thread(self.dotimeout, (timeout, self.ticket)) + return self.ticket + + def check(self, ievent): + + """ check if there are wait items for ievent .. check if 'catch' + matches on ievent.postfix if so put ievent on queue. """ + + cmnd = ievent.cmnd + for item in self.waitlist: + if item[0] == cmnd: + if cmnd == "JOIN": + catch = ievent.txt + ievent.postfix + else: + catch = ievent.nick + ievent.postfix + if item[1] in catch: + ievent.ticket = item[3] + item[2].put_nowait(ievent) + self.delete(ievent.ticket) + rlog(1, 'wait', 'got response for %s' % item[0]) + ievent.isresponse = True + + def dotimeout(self, timeout, ticket): + + """ start timeout thread for wait with ticket nr. """ + + rlog(1, 'wait', 'starting timeouthread for %s' % str(ticket)) + time.sleep(float(timeout)) + self.delete(ticket) + + @locked + def delete(self, ticket): + + """ delete wait item with ticket nr. """ + + for itemnr in range(len(self.waitlist)-1, -1, -1): + if self.waitlist[itemnr][3] == ticket: + self.waitlist[itemnr][2].put_nowait(None) + del self.waitlist[itemnr] + rlog(1, 'wait', 'deleted ' + str(ticket)) + return 1 + +class Privwait(Wait): + + """ wait for privmsg .. catch is on nick """ + + def register(self, catch, queue, timeout=15): + + """ register wait for privmsg. """ + + rlog(1, 'privwait', 'registering for ' + catch) + return Wait.register(self, 'PRIVMSG', catch, queue, timeout) + + def check(self, ievent): + + """ check if there are wait items for ievent. """ + + for item in self.waitlist: + if item[0] == 'PRIVMSG': + if ievent.userhost == item[1]: + ievent.ticket = item[3] + item[2].put_nowait(ievent) + self.delete(ievent.ticket) + rlog(1, 'privwait', 'got response for %s' % item[0]) + ievent.isresponse = True + +class Jabberwait(Wait): + + """ wait object for jabber messages. """ + + def register(self, catch, queue, timeout=15): + + """ register wait for privmsg. """ + + rlog(1, 'jabberwait', 'registering for %s' % catch) + self.ticket += 1 + self.waitlist.append((catch, queue, self.ticket)) + if timeout: + thr.start_new_thread(self.dotimeout, (timeout, self.ticket)) + return self.ticket + + def check(self, msg): + + """ check if is waited for. """ + + for teller in range(len(self.waitlist)-1, -1, -1): + i = self.waitlist[teller] + if i[0] == msg.userhost: + msg.ticket = i[2] + i[1].put_nowait(msg) + self.delete(msg.ticket) + rlog(10, 'jabberwait', 'got response for %s' % i[0]) + msg.isresponse = 1 + + @locked + def delete(self, ticket): + + """ delete wait item with ticket nr. """ + + for itemnr in range(len(self.waitlist)-1, -1, -1): + item = self.waitlist[itemnr] + if item[2] == ticket: + item[1].put_nowait(None) + try: + del self.waitlist[itemnr] + rlog(1, 'jabberwait', 'deleted ' + str(ticket)) + except IndexError: + pass + return 1 + +class Jabbererrorwait(Jabberwait): + + """ wait for jabber errors. """ + + def check(self, msg): + + """ check if is waited for. """ + + if not msg.getType() == 'error': + return + + errorcode = msg.getErrorCode() + + for teller in range(len(self.waitlist)-1, -1, -1): + i = self.waitlist[teller] + if i[0] == 'ALL' or i[0] == errorcode: + msg.error = msg.getError() + msg.ticket = i[2] + i[1].put_nowait(msg) + self.delete(msg.ticket) + rlog(10,'jabbererrorwait','got error response for %s' % i[0]) --- gozerbot-0.99.1.orig/build/lib/gozerbot/stats.py +++ gozerbot-0.99.1/build/lib/gozerbot/stats.py @@ -0,0 +1,67 @@ +# gozerbot/stats.py +# +# + +""" maintain bot stats. """ + +## IMPORT SECTION + +from gozerbot.utils.statdict import Statdict + +## END IMPORT + +## LOCK SECTION + +# no locks + +## END LOCK + +class GozerStats(object): + + """ dict containing all gozerbot related stats. """ + + def __init__(self): + self.data = {} + + def init(self, item): + + """ initialize a stats item. """ + + self.data[item] = Statdict() + + def up(self, item, issue): + + """ up a stats item. """ + + if not self.data.has_key(item): + self.init(item) + self.data[item].upitem(issue) + + def get(self, item): + + """ return stats item. """ + + try: + return self.data[item] + except KeyError: + return + + def list(self, item): + + """ list all stats belonging to item. """ + + if self.data.has_key(item): + return self.data[item].keys() + + def all(self): + + """ list all stats items. """ + + return self.data.keys() + +## INIT SECTION + +# the gozerbot stats object +stats = GozerStats() + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/botbase.py +++ gozerbot-0.99.1/build/lib/gozerbot/botbase.py @@ -0,0 +1,394 @@ +# gozerbot/botbase.py +# +# + +""" bot base class. provides data/methods common to all bots. """ + +# IMPORT SECTION + +# gozerbot imports +from threads.thr import start_new_thread +from utils.log import rlog +from less import Less +from persist.pdol import Pdol +from utils.dol import Dol +from utils.lazydict import LazyDict +from persist.persiststate import PersistState +from channels import Channels +from datadir import datadir +from persist.pdod import Pdod +from config import config, Config, fleetbotconfigtxt +from runner import runners_start +from monitor import Monitor +from callbacks import callbacks, gn_callbacks +from cache import userhosts +from wait import Wait, Privwait +from eventhandler import mainhandler +from exit import globalshutdown +from utils.exception import handle_exception +from plugins import plugins + +# throttle support +from gozerbot.plugs.throttle import state as throttlestate + +# basic imports +import time, threading, os, types, sys, copy + +# END IMPORT + +cpy = copy.deepcopy + +class BotBase(object): + + """ + + Base class for all bots. Inherit from this. + + :param cfg: configuration + :type cfg: dict like + + """ + + def __init__(self, name, cfg={}): + self.name = name + self.encoding = sys.getdefaultencoding() + + # if cfg is not passed on create one + if not cfg: + cfg = Config(inittxt=fleetbotconfigtxt) + + if not cfg.has_key('dir'): + cfg['dir'] = os.getcwd() + + if not cfg.has_key('user'): + cfg['user'] = 'gozerbot' + + if not cfg.has_key('type'): + cfg['type'] = 'gozernet' + + # set attributes based on config + self.__dict__.update(cfg) + + # if name not set in config file use the directory name + + if not cfg.has_key('name'): + if 'fleet' in cfg['dir']: + self.name = cfg.dir.split(os.sep)[-1] + else: + self.name = name + + # default nick to gozerbot + if not cfg.has_key('nick'): + self.nick = 'gozerbot' + + if not cfg.has_key('server'): + self.server = 'server not set' + + try: + self.host = cfg['host'] + if not self.host: + self.host = self.user.split('@')[1] + except (KeyError, IndexError): + try: + self.host = self.user.split('@')[1] + except (ValueError, IndexError): + self.host = 'host not set' + + + # default port to 0 (use default port) + if not cfg.has_key('port'): + self.port = 0 + + if not cfg.has_key('ipv6'): + self.ipv6 = 0 + else: + self.ipv6 = cfg['ipv6'] + + # make sure bot name is not a directory + if '..' in self.name or '/' in self.name: + raise Exception('wrong bot name %s' % self.name) + + # set datadir to datadir/fleet/ + self.datadir = datadir + os.sep + 'fleet' + os.sep + self.name + + # set datadir to datadir/fleet/ + if hasattr(os, 'mkdir'): + if not os.path.exists(self.datadir): + os.mkdir(self.datadir) + + # bot state + self.state = Pdod(self.datadir + os.sep + 'state') # bot state + + # joined channels list .. used to join channels + if not self.state.has_key('joinedchannels'): + self.state['joinedchannels'] = [] + + # allowed commands on the bot + if not self.state.has_key('allowed'): + self.state['allowed'] = [] + + # channels we dont want ops in + if not self.state.has_key('no-op'): + self.state['no-op'] = [] + + # channels we are op in + if not self.state.has_key('opchan'): + self.state['opchan'] = [] + + # the time we joined a channel + self.timejoined = {} + + # jabber type doesnt exists anymore .. set type to 'xmpp' instead + self.type = self.type or 'gozernet' + + if self.type == 'jabber': + self.type = 'xmpp' + + self.networkname = self.server + self.jid = "%s@%s" % (self.nick, self.server) + self.jids = {} + self.shutloop = False + self.cfg = cfg # the bots config + self.orignick = "" # original nick + self.blocking = 1 # use blocking sockets + self.lastoutput = 0 # time of last output + self.stopped = False # flag to set when bot is to be stopped + self.connected = False # conencted flag + self.connecting = False # connecting flag + self.connectok = threading.Event() # event set when bot has connected + self.waitingforconnect = False # flag to indicate we are waiting for connect + self.starttime = time.time() # start time of the bot + self.nrevents = 0 # number of events processed + self.gcevents = 0 # number of garbage collected events + self.less = Less(5) # output buffering + self.userchannels = Dol() # list of channels a user is in + self.channels = Channels(self.datadir + os.sep + 'channels') # channels + self.userhosts = PersistState(self.datadir + os.sep + 'userhosts') # userhosts cache + self.splitted = [] # list of splitted nicks + self.throttle = [] # list of nicks that need to be throttled + self.jabber = False # flag is set on jabber bots + self.google = False + if 'google' in self.host: + self.google = True # flag is set on google bots + try: + import google.appengine.ext + self.google = True + except: + pass + self.callbacks = callbacks + self.monitor = Monitor() + self.wait = Wait() + self.privwait = Privwait() + self.error = None + + # start runners + runners_start() + #self.monitor.start() + + def ownercheck(self, ievent, txt=None): + + """ + check whether an event originated from the bot owner. + + :param ievent: event to check for owner with + :param txt: optional txt to report to user when check fails + :rtype: 1 or 0 + + .. literalinclude:: ../../gozerbot/botbase.py + :pyobject: BotBase.ownercheck + + """ + + # use owner set in bot's config or else in global config + owner = self.cfg['owner'] or config['owner'] + + # check if event userhost in in owner .. check lists and string values + if type(owner) == types.ListType: + if ievent.userhost in owner: + return 1 + elif owner == ievent.userhost: + return 1 + else: + rlog(100, self.name, 'failed owner check %s should be in %s' % (ievent.userhost, owner)) + if not txt: + ievent.reply("only owner (see config file) is allowed to perform this command") + else: + ievent.reply("only owner (see config file) %s" % txt) + return 0 + + def save(self): + + """ save bot state. """ + + self.channels.save() + self.userhosts.save() + self.state.save() + + def stop(self): + + """ stop the bot. """ + + self.stopped = True + rlog(10, self.name, 'stopped') + + def exit(self): + + """ shutdown the bot. overload this. """ + + pass + + def connect(self, reconnect=True): + + """ connect the bot to the server. reconnects in the default case. """ + + pass + + def say(self, printto, what, event=None, who=None, how='msg', fromm=None, speed=0, groupchat=False): + print what + + def whois(self, nick): + pass + + def sendraw(self, txt): + print txt + + def voice(self, channel, txt): + pass + + def action(self, channel, txt): + pass + + def _raw(self, txt): + print txt + + def settopic(self, channel, txt): + pass + + def names(self, channel): + pass + + def gettopic(self, channel): + pass + + def _dcclisten(self, *args): + pass + + def donick(self, nick, save=False, setorig=True): + pass + + def fakein(self, txt): + pass + + def part(self, channel): + pass + + def serveforever(self): + self.stopped = False + self.shutloop = False + + while not self.stopped and not self.shutloop: + try: + import asyncore + asyncore.poll(timeout=0.01) + except ImportError: + pass + except Exception, ex: + handle_exception() + globalshutdown() + os._exit(1) + time.sleep(0.01) + mainhandler.handle_one() + + def join(self, channel, password=""): + pass + + def joinchannels(self): + + """ join all registered channels. overload this. """ + + pass + + def connectwithjoin(self, reconnect=True): + + """ connect to the server and join channels. """ + + self.connect(reconnect) + self.connectok.wait() + start_new_thread(self.joinchannels, ()) + + def broadcast(self): + + """ announce a message to all channels. overload this""" + + pass + + def send(self, txt): + + """ send txt to the server. overload this""" + + pass + + def shutdown(self): + + """ close sockets of the bot. overload this""" + + pass + + def domsg(self, msg, response=False, wait=False): + + """ + excecute a message (txt line) on the bot. + + :param msg: text line to execute + :type msg: string + :rtype: None + + .. literalinclude:: ../../gozerbot/botbase.py + :pyobject: BotBase.domsg + """ + + if response: + msg.reply('executing %s (%s) on %s bot' % (msg.txt, msg.userhost, self.name)) + + from gozerbot.plugins import plugins + + if wait: + plugins.waitdispatch(self, msg) + else: + plugins.trydispatch(self, msg) + + def ratelimit(self, userhost, cpm=20): + try: + throttlestate['level'][userhost] = cpm + throttlestate.save() + rlog(10, self.name, '%s throttled to %s cpm' % (userhost, cpm)) + except Exception, ex: + rlog(100, self.name, "can't set throttle of %s" % userhost) + handle_exception() + + def remoteout(self, request, event): + request.wfile.write(event.tojson()) + + def doevent(self, event): + + """ + dispatch an event. + + :param event: event to dispatch. + :rtype: None + + """ + + if not event: + return + + e = cpy(event) + + if event.isremote: + rlog(10, self.name, 'remote event .. calling gn_callbacks') + gn_callbacks.check(self, e) + else: + callbacks.check(self, e) + + if event.remotecmnd: + plugins.trydispatch(self, event) --- gozerbot-0.99.1.orig/build/lib/gozerbot/gozerimport.py +++ gozerbot-0.99.1/build/lib/gozerbot/gozerimport.py @@ -0,0 +1,97 @@ +# gozerbot/myimport.py +# +# + +""" use the imp module to import modules. """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# basic import +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec + +import time, sys, imp, os, thread + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +gozerimportlock = thread.allocate_lock() +locked = lockdec(gozerimportlock) + +# END LOCK +# ======== + +#@locked +def gozer_import(name, path=None): + + """ + import module with the imp module .. will reload module is + already in sys.modules. + + :param name: name of the module to import (may contain dots) + :type name: string + :param path: optional path to search in + :type path: string + :rtype: module + + .. literalinclude:: ../../gozerbot/gozerimport.py + :pyobject: gozer_import + + """ + + rlog(1, 'gozerimport', 'importing %s' % name) + + splitted = name.split('.') + + for plug in splitted: + fp, pathname, description = imp.find_module(plug, path) + try: + result = imp.load_module(plug, fp, pathname, description) + try: + path = result.__path__ + except: + pass + finally: + if fp: + fp.close() + + if result: + return result + +#@locked +def force_import(name): + + """ + force import of module by replacing it in sys.modules. + + :param name: name of module to import + :type name: string + :rtype: module + + .. literalinclude:: ../../gozerbot/gozerimport.py + :pyobject: force_import + + """ + + try: + del sys.modules[name] + except KeyError: + pass + plug = gozer_import(name) + if plug: + sys.modules[name] = plug + return plug + +# ============ +# INIT SECTION + +# no vars + +# END INIT +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/redispatcher.py +++ gozerbot-0.99.1/build/lib/gozerbot/redispatcher.py @@ -0,0 +1,394 @@ +# gozerbot/redispatcher.py +# +# + +""" implement RE (regular expression) dispatcher. """ + +__copyright__ = 'this file is in the public domain' + +## IMPORT SECTION + +# gozerbot imports +from config import config +from utils.log import rlog +from utils.trace import calledfrom +from utils.exception import handle_exception +from utils.locking import lockdec +from runner import cmndrunners +import threads.thr as thr + +# basic imports +import sys, re, copy, types, thread + +## END IMPORT + +## LOCK SECTION + +# locks +relock = thread.allocate_lock() +locked = lockdec(relock) + +## END LOCK + +class RECallback(object): + + """ + a regular expression callback. + + :param index: index into the callback list + :type index: integer + :param regex: the regex to match + :type regex: string + :param func: the callback function + :type func: function + :param perm: permissions of the callback + :type perm: list .. list of permissions + :param speed: speed at which the callback should be executed + :type speed: integer + :param threaded: whether the callback should executed in its own thread + :type threaded: boolean + :param allowqueue: whether this command is allowed in pipelines + :type allowqueue: boolean + :param options: options allowed for this command + :type options: dict + + """ + + def __init__(self, index, regex, func, perm, plugname, speed=5, \ +threaded=True, allowqueue=True, options={}): + self.name = thr.getname(func) # name of the callback + self.index = index # index into the list + self.regex = regex # the RE to match + self.compiled = re.compile(regex) # compiled RE + self.func = func # the function to call if RE matches + # make sure perms is a list + if type(perm) == types.ListType: + self.perms = list(perm) + else: + self.perms = [perm, ] + # plug name + self.plugname = plugname # plugname where RE callbacks is registered + self.speed = copy.deepcopy(speed) # speed at which the function runs + self.threaded = copy.deepcopy(threaded) # set when run threaade + self.allowqueue = copy.deepcopy(allowqueue) # set when pipeline is allowed + self.options = dict(options) # options set on the callback + self.activate = True + +class REDispatcher(object): + + """ + this is were the regexs callbacks live. + + """ + + def __init__(self): + self.relist = [] + + def size(self): + + """ + nr of callbacks. + + """ + + return len(self.relist) + + def activate(self, plugname): + for i in self.relist: + if i.plugname == plugname: + i.activate = True + + def disable(self, plugname): + for i in self.relist: + if i.plugname == plugname: + i.activate = False + + def whatperms(self): + + """ + return possible permissions. + + :rtype: list .. list of possible permissions + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.whatperms + + """ + + result = [] + + for i in self.relist: + for j in i.perms: + if j not in result: + result.append(j) + + return result + + def list(self, perm): + + """ + list RECallbacks with permission perm. + + :param perm: permission to check for + :type perm: string + :rtype: list .. list of RECallbacks + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.list + """ + + result = [] + perm = perm.upper() + + for recom in self.relist: + if perm in recom.perms: + result.append(recom) + + return result + + def getfuncnames(self, plug): + + """ + return function names in plugin. + + :param plug: name of the plugin to get callbacks of + :type plug: string + :rtype: list .. list of function names + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.getfuncnames + + """ + + result = [] + for i in self.relist: + if i.plugname == plug: + result.append(i.func.func_name) + return result + + def permoverload(self, funcname, perms): + + """ + overload permission of function with funcname. + + :param funcname: name of the function to overload + :type funcname: string + :param perms: permission to overload + :type perms: list + :rtype: boolean .. whether the overload succeeded + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.permoverload + + """ + + perms = [perm.upper() for perm in perms] + got = 0 + + for nr in range(len(self.relist)): + + try: + if self.relist[nr].func.func_name == funcname: + self.relist[nr].perms = list(perms) + rlog(0, 'redispatcher', '%s function overloaded with %s' \ +% (funcname, perms)) + got = 1 + + except AttributeError: + rlog(10, 'redispatcher', 'permoverload: no %s function' % \ +funcname) + if got: + return True + + return False + + def add(self, index, regex, func, perm, speed=5, threaded=True, allowqueue=True, options={}): + + """ + add a regular expression command. + + :param index: index into the callback list + :type index: integer + :param regex: the regex to match + :type regex: string + :param func: the callback function + :type func: function + :param perm: permissions of the callback + :type perm: list .. list of permissions + :param speed: speed at which the callback should be executed + :type speed: integer + :param threaded: whether the callback should executed in its own thread + :type threaded: boolean + :param allowqueue: whether this command is allowed in pipelines + :type allowqueue: boolean + :param options: options allowed for this command + :type options: dict + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.add + + """ + + try: + # get plugin name from where callback is added + plugname = calledfrom(sys._getframe()) + + if config['loadlist'] and plugname not in config['loadlist']: + return + # add Recallback + + self.relist.append(RECallback(index, regex, func, perm, plugname, \ +speed, threaded, allowqueue, options)) + # sort of index number + self.relist.sort(lambda a, b: cmp(a.index, b.index)) + rlog(0, 'redispatcher', 'added %s (%s) ' % (regex, plugname)) + + finally: + pass + + def unload(self, plugname): + + """ + unload regex commands. + + :param plugname: name of the plugins to unload callbacks from + :type plugname: string + :rtype: boolean .. whether the unloading succeeded + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.unload + + """ + + got = False + try: + + for i in range(len(self.relist)-1, -1 , -1): + if self.relist[i].plugname == plugname: + rlog(1, 'redispatcher', 'unloading %s (%s)' % \ +(self.relist[i].regex, plugname)) + del self.relist[i] + got = True + finally: + pass + return got + + def getcallback(self, txt): + + """ + get re callback if txt matches. + + :param txt: txt to match against the regular expressions + :type txt: string + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.getcallback + """ + + for i in self.relist: + + try: + result = re.search(i.compiled, txt) + + if result: + return i + + except: + pass + + def dispatch(self, callback, txt, wait=False): + + """ + try to dispatch callback on txt. + + :param callback: the callback to fire + :type callback: RECallback + :param txt: txt to match the regular expression + :type txt: string + :param wait: whether to wait for the result + :type wait: boolean + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: REDispatcher.dispatch + + """ + + try: + result = re.search(callback.compiled, txt) + + if result: + + if callback.threaded: + thread = thr.start_new_thread(callback.func, (txt, result.groups())) + + if wait: + thread.join() + + else: + cmndrunners.put(callback.plugname, callback.func, txt, \ +result.groups()) + + return 1 + + except Exception, ex: + handle_exception() + +class BotREDispatcher(REDispatcher): + + """ + dispatcher on Event. + + """ + + def dispatch(self, callback, bot, ievent, wait=False): + + """ + dispatch callback on ircevent. + + :param callback: the callback to fire + :type callback: RECallback + :param bot: the bot on which the callback was triggered + :type bot: gozerbot.botbase.BotBase + :param ievent: the event that triggered the callback + :type ievent: gozerbot.eventbase.EventBase + :rtype: boolean .. whether the dispatch was succesful + + .. literalinclude:: ../../gozerbot/redispatcher.py + :pyobject: BotREDispatcher.dispatch + + """ + + if not self.activate: + return False + + try: + result = re.search(callback.compiled, ievent.txt.strip()) + + if result: + ievent.groups = list(result.groups()) + + if callback.threaded or ievent.threaded: + thread = thr.start_bot_command(callback.func, (bot, ievent)) + + if thread and wait: + thread.join() + + else: + cmndrunners.put(callback.plugname, callback.func, bot, \ +ievent) + return True + + except Exception, ex: + handle_exception(ievent) + + return False + +## INIT SECTION + +# dispatcher before commands are checked +rebefore = BotREDispatcher() + +# dispatcher after commands are checked +reafter = BotREDispatcher() + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/eventbase.py +++ gozerbot-0.99.1/build/lib/gozerbot/eventbase.py @@ -0,0 +1,602 @@ +# gozerbot/eventbase.py +# +# + +""" + This module implements the EventBase class from which all other + events inherit. + + :vars: defaultevent + +""" + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports +from xmpp.core import XMLDict +from utils.log import rlog +from utils.generic import stripident, fix_format, fromenc, toenc +from utils.lazydict import LazyDict +from gozerbot.stats import stats +from gozerbot.config import config + +# basic imports +import time, re, types, copy + +from simplejson import loads, dumps + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +## START + +# used to copy data +cpy = copy.deepcopy + +def makeargrest(event): + + """ + set arguments and rest attributes of an event. + + :param event: event to manipulate + + """ + + # arguments + try: + event.args = event.txt.split()[1:] + except ValueError: + event.args = [] + + # txt after command + try: + cmnd, event.rest = event.txt.split(' ', 1) + except ValueError: + event.rest = "" + + # the command + event.command = event.txt.split(' ')[0] + +class EventBase(XMLDict): + + """ + Base class for all events. Inherit from this. + + :param event: event to clone from (optional) + :type event: gozerbot.event.EventBase + :param bot: bot on which this event occured + :type bot: gozerbot.botbase.BotBase + + """ + + + def __init__(self, event={}, bot=None): + self.server = "" + XMLDict.__init__(self) + self.threaded = False + self.onlyqueues = False + self.orig = event # the original data .. SET BY HANDLER + self.type = 'chat' # type of event + self.cbtype = '' # callback type + self.jabber = False # set if event is jabber event + self.groupchat = False # set if event is groupchat + self.botoutput = False # set if event is bot output + self.cmnd = None # the event command + self.prefix = u"" # txt before the command + self.postfix = u"" # txt after the command + self.target = u"" # target to give reponse to + self.arguments = [] # arguments of event + self.nick = u"" # nick of user originating the event + self.user = u"" # user originating the event + self.ruserhost = u"" # real userhost + self.userhost = u"" # userhost that might change + self.stripped = u"" # stripped JID + self.resource = u"" # resource part of JID + self.origin = u"" # source of the event + self.origchannel = u"" # original channel + self.channel = u"" # channel .. might change + self.origtxt = u"" # original txt + self.txt = u"" # text .. might change + self.command = u"" # the bot command if any + self.remoteout = u"" + self.remotecmnd = False + self.usercmnd = False + self.alias = u"" # set to alias if one is used + self.aliased = u"" # set if commadn is aliased + self.time = time.time() # event creation time + self.msg = False # set if event is a private message + self.args = [] # arguments of command + self.rest = u"" # txt following the command + self.usercmnd = 0 # set if event is a command + self.bot = bot # the bot where the event originated on + self.sock = None # socket (set in DCC chat) + self.allowqueue = True # allow event to be used in pipeline + self.closequeue = True # cloase event queues when execution has ended + self.inqueue = None # queue used as input in pipeline + self.queues = [] # output queues (only used when set) + self.printto = None # target to printto + self.speed = 0 # speed with which this event needs to be processed + self.groups = None # set if event triggers a RE callback + self.cc = u"" # control character + self.jid = None # JID of used originating the event + self.jidchange = None # set is event changes jid + self.conn = None # connection of bot originating the event + self.denied = False # set if command is denied + self.iscallback = False + self.isresponse = False # set if event is a reponse + self.isrelay = False + self.isremote = False + self.isdcc = False # set if event is DCC related + self.isctcp = False # set if event is an ACTION + self.options = LazyDict() # options dict on the event + self.optionset = [] # list of options set + self.filter = [] # filter list to use on output + + # wave stuff + self.wave = False + self.properties = None + self.context = None + self.eventin = None + self.wavelet = None + + # GAE stuff + self.google = False + self.xmppgae = False + self.response = None + self.request = None + + # xmpp stuff + self.host = self.server + self.xml = u"" + self.realjid = u"" + self.fromm = u"" + self.to = u"" + self.type = u"" + self.id = 0 + self.subject = u"" + self.body = u"" + self.error = u"" + self.errorcode = u"" + self.html = u"" + self.thread = u"" + self.x = {} + + if event: + self.copyin(event) + + self.toirc() + + # stats + stats.up('events', 'created') + + def __copy__(self): + return EventBase(self) + + def __deepcopy__(self, bla): + return EventBase(self) + + def toirc(self): + + """ + set ircevent compat attributes. + + .. literalinclude:: ../../gozerbot/eventbase.py + :pyobject: EventBase.toirc + """ + + self.jidchange = False + self.cmnd = 'Message' + + try: + self.resource = self.fromm.split('/')[1] + except IndexError: + pass + + self.channel = self['fromm'].split('/')[0] + self.origchannel = self.channel + self.nick = self.resource + + #try: + # self.jid = bot.jids[self.channel][self.resource] + # self.jidchange = True + #except KeyError: + # pass + + self.jid = self.fromm + self.ruserhost = self.jid + self.userhost = self.jid + self.stripped = self.jid.split('/')[0] + self.printto = self.channel + self.target = self.printto + + for node in self.subelements: + try: + self.txt = node.body.data + except (AttributeError, ValueError): + continue + + self.origtxt = self.txt + self.time = time.time() + + if self.type == 'groupchat': + self.groupchat = True + if self.jidchange: + self.userhost = self.stripped + else: + self.groupchat = False + self.userhost = self.stripped + + self.msg = not self.groupchat + + def copyin(self, ievent): + + """ + copy in an event. + + :param ievent: event to copy in + :type ievent: EventBase + + .. literalinclude:: ../../gozerbot/eventbase.py + :pyobject: EventBase.copyin + + """ + self.update(ievent) + + if ievent.has_key('options'): + self.options = dict(ievent.options) + + if ievent.has_key('queues'): + self.queues = list(ievent.queues) + + return self + + def filtered(self, txt): + + """ + see if txt if filtered on this event. + + :param txt: text to check if its filtered + :type txt: string + + .. literalinclude:: ../../gozerbot/eventbase.py + :pyobject: EventBase.filtered + + """ + + if not self.filter: + return False + + for filter in self.filter: + if filter in txt: + return False + + return True + + def parse(self, bot, rawstr): + + """ + parse raw string into event. overload this. + + :param bot: bot on which event is triggered + :type bot: gozerbot.botbase.BotBase + :param rawstr: string as recieved on the socket + :type rawstr: string + + """ + + pass + + def make_response(self, txt, result=None, nick=None, dot=False, nritems=False, nr=False, fromm=None, private=False, how=''): + + + # don't reply is result is empty list + if result == []: + return + + # stats + stats.up('events', 'replies') + if not how: + try: + how = self.options['--how'] + except KeyError: + how = 'msg' + + # init + restxt = "" + splitted = [] + + # make reply if result is a dict + if type(result) == types.DictType: + for i, j in result.iteritems(): + if type(j) == types.ListType: + try: + z = ' .. '.join(j) + except TypeError: + z = unicode(j) + else: + z = j + res = "%s: %s" % (i, z) + splitted.append(res) + if dot == True: + restxt += "%s%s" % (res, ' .. ') + else: + restxt += "%s %s" % (dot or ' ', res) + if restxt: + if dot == True: + restxt = restxt[:-6] + elif dot: + restxt = restxt[:-len(dot)] + + lt = False # set if result is list + + # set vars if result is a list + if type(txt) == types.ListType and not result: + result = txt + origtxt = u"" + lt = True + else: + origtxt = txt + + if result: + lt = True + + # if queues are set write output to them + if self.queues: + for i in self.queues: + if splitted: + for item in splitted: + i.put_nowait(item) + elif restxt: + i.put_nowait(restxt) + elif lt: + for j in result: + i.put_nowait(j) + else: + i.put_nowait(txt) + if self.onlyqueues: + return + + # check if bot is set in event + if not self.bot: + rlog(10, 'event', 'no bot defined in event') + return "no bot is defined in this event" + + # make response + pretxt = origtxt + if lt and not restxt: + res = [] + + # check if there are list in list + + for i in result: + if type(i) == types.ListType or type(i) == types.TupleType: + try: + res.append(u' .. '.join(i)) + except TypeError: + res.extend(i) + else: + res.append(i) + + # if nritems is set .. + result = res + if nritems: + if len(result) > 1: + pretxt += "(%s items) .. " % len(result) + txtlist = result + + # prepend item number for results + if not nr is False: + try: + start = int(nr) + except ValueError: + start = 0 + txtlist2 = [] + teller = start + for i in txtlist: + txtlist2.append(u"%s) %s" % (teller, i)) + teller += 1 + txtlist = txtlist2 + + # convert results to encoding + txtl = [] + for item in txtlist: + txtl.append(toenc(item)) + txtlist = txtl + + # join result with dot + if dot == True: + restxt = ' .. '.join(txtlist) + elif dot: + restxt = dot.join(txtlist) + else: + restxt = ' '.join(txtlist) + + # see if txt needs to be prepended + if pretxt: + try: + restxt = pretxt + restxt + except TypeError: + rlog(10, 'eventbase', "can't add %s and %s" % (str(pretxt), str(restxt))) + + # if txt in result is filtered ignore the reuslt + if self.filtered(restxt): + return + + if restxt: + return restxt + else: + return "no data found" + + def reply(self, *args, **kwargs): + + """ + reply to event. this version prints to stdout + + :param txt: txt to reply + :type txt: string + :param result: result list .. list of items to reply (is prepended to txt) + :type result: string + :param nick: nick to reply to + :type nick: string + :param dot: whether results of result list should be seperated with a dot with ' ..' or the txt passed in as dot argument + :type dot: boolean or string + :param nritems: whether results should be numbered + :type nritems: boolean + :param nr: the number to start numerbering the result with + :type nr: integer + :param fromm: the user sending the reply + :type fromm: nick or JID + :param private: whether the reply should be in private + :type private: boolean + :param how: how the reply should be made (msg, notice, ctcp) + :type how: string + + """ + resp = self.make_response(*args, **kwargs) + + if not resp: + return + + self.bot.say(self.channel, resp) + + def missing(self, txt): + + """ + show what arguments are missing. + + :param txt: txt to add to missing response + :type txt: string + + .. literalinclude:: ../../gozerbot/eventbase.py + :pyobject: EventBase.missing + + """ + + stats.up('events', 'missing') + if self.origtxt: + splitted = self.origtxt.split() + if self.bot.nick in splitted[0]: + try: + cmnd = splitted[1] + except IndexError: + cmnd = splitted[0] + elif 'cmnd' in splitted[0]: + try: + cmnd = splitted[2] + except IndexError: + cmnd = splitted[0] + else: + if self.msg: + cmnd = splitted[0] + else: + if self.aliased: + cmnd = self.aliased + else: + cmnd = splitted[0][1:] + self.reply(cmnd + ' ' + txt) + else: + self.reply('missing origtxt: %s' % txt) + + def ircstr(self): + + """ + old compat function. use str() now. + + """ + + return str(self) + + def handle_error(self, data): + + """ + function to handler errors. override this. + + :param data: txt describing the error (can be different with inherited classes) + :tyoe data: string (can be different with inherited classes) + + """ + + rlog(10, self.name.error, 'ERROR: %s' % str(data)) + + def done(self, txt=None): + + """ + reply with the txt 'done'. + + :param txt: txt to prepend to 'done' + :type txt: string + + """ + + if txt: + self.reply('%s done' % txt) + else: + self.reply('done') + + def tojson(self): + new = cpy(self) + new.sock = 'setremote' + new.bot = 'setremote' + new.request = 'setremote' + new.response = 'setremote' + new.queues = [] + result = dumps(new) + rlog(0, 'eventbase', 'tojson - %s' % str(result)) + return result + + def fromjsonstring(self, input): + temp = loads(input) + rlog(10, 'eventbase', str(temp)) + self.update(temp) + return self + + def checkqueues(self, resultlist): + + """ + check if resultlist is to be sent to the queues. if so do it + + :param resultlist: list of results to send to queues + :type resultlist: list + :rtype: boolean + + """ + + if self.queues: + + for queue in self.queues: + for item in resultlist: + queue.put_nowait(item) + + return True + + def subelement(self, name): + if self.subelements: + rlog(10, 'eventbase', "got subelements") + for node in self.subelements: + try: + attr = getattr(node, name) + rlog(10, 'eventbase', "got subelement %s" % (name, str(attr))) + return attr.data + except (AttributeError, TypeError): + continue + +# ============ +# INIT SECTION + +# default event used to initialise events +defaultevent = EventBase() + + +# END INIT +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/dbusers.py +++ gozerbot-0.99.1/build/lib/gozerbot/dbusers.py @@ -0,0 +1,375 @@ +# gozerbot/dbusers.py +# +# + +""" bots users for mysql interface""" + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.log import rlog +from gozerbot.database.db import db +import types + +class Dbusers(object): + + """ users class """ + + def __init__(self): + self.db = db + + def size(self): + """ return nr of users """ + result = self.db.execute(""" SELECT DISTINCT COUNT(*) FROM userhosts """) + if result: + return result[0][0] + + def getperms(self, userhost): + """ return permission of user""" + name = self.getname(userhost) + if not name: + return ['ANON', ] + result = self.db.execute(""" SELECT perm FROM perms WHERE name = %s """, name) + res = [] + for i in result: + res.append(i[0]) + return res + + def exist(self, name): + """ see if user with exists """ + name = name.lower() + result = self.db.execute(""" SELECT name,userhost FROM userhosts WHERE name = %s """, name) + return result + + def getname(self, userhost): + """ get name of user belonging to """ + result = self.db.execute(""" SELECT name FROM userhosts WHERE %s LIKE userhost """, userhost) + if result: + return result[0][0] + + def add(self, name, userhosts, perms): + """ add an user """ + if type(userhosts) != types.ListType: + rlog(10, 'dbusers', 'i need a list of userhosts') + return 0 + for i in userhosts: + self.adduserhost(name, i) + for i in perms: + self.addperm(name, i) + rlog(10, 'users', '%s added to user database' % name) + return 1 + + def adduserhost(self, name, userhost): + """ add userhost """ + name = name.lower() + res = None + result = self.db.execute(""" INSERT INTO userhosts(name, userhost) values(%s, %s) """, (name, userhost)) + if result: + res = 1 + rlog(10, 'users', '%s (%s) added to userhosts' % (name, userhost)) + return res + + def addperm(self, name, perm): + """ add permission """ + name = name.lower() + perm = perm.upper() + res = None + result = self.db.execute(""" INSERT INTO perms(name, perm) values(%s, %s) """, (name, perm)) + if result: + res = 1 + rlog(10, 'users', '%s perm %s added' % (name, perm)) + return res + + def addstatus(self, name, status): + """ add permission """ + name = name.lower() + status = status.upper() + res = None + result = self.db.execute(""" INSERT INTO statuses(name, status) values(%s, %s) """, (name, status)) + if result: + res = 1 + rlog(10, 'users', '%s status %s added' % (name, status)) + return res + + def addpermit(self, name, who, what): + """ add permission """ + p = '%s %s' % (who, what) + res = None + result = self.db.execute(""" INSERT INTO permits(name, permit) values(%s, %s) """, (name, p)) + if result: + res = 1 + rlog(10, 'users', '%s permit %s added' % (name, p)) + return res + + def delperm(self, name, perm): + """ add permission """ + name = name.lower() + perm = perm.upper() + result = self.db.execute(""" DELETE FROM perms WHERE name = %s AND perm = %s """, (name, perm)) + if result: + rlog(10, 'users', '%s perm %s deleted' % (name, perm)) + return result + + def permitted(self, userhost, who, what): + """ check if (who,what) is in users permit list """ + name = self.getname(userhost) + res = None + if name: + result = self.db.execute(""" SELECT permit FROM permits WHERE name = %s """, name) + if result: + for i in result: + if "%s %s" % (who, what) == i[0]: + res = 1 + return res + + def names(self): + """ get names of all users """ + res = [] + result = self.db.execute(""" SELECT DISTINCT name FROM userhosts """) + if result: + for i in result: + res.append(i[0]) + return res + + def merge(self, name, userhost): + """ add userhosts to user with name """ + name = name.lower() + if not self.exist(name): + return 0 + res = None + result = self.db.execute(""" INSERT INTO userhosts(userhost, name) VALUES (%s, %s) """, (userhost, name)) + if result: + res = 1 + return res + + def deluserpermit(self, name, permit): + """ delete user with name """ + name = name.lower() + res = None + p = '%s %s' % permit + return self.db.execute(""" DELETE FROM permits WHERE name = %s AND permit = %s """, (name, p)) + + def deluserhost(self, name, userhost): + """ delete user with name """ + name = name.lower() + res = None + return self.db.execute(""" DELETE FROM userhosts WHERE name = %s AND userhost = %s """, (name, userhost)) + + def deluserperms(self, name, perm): + """ delete user with name """ + name = name.lower() + res = None + return self.db.execute(""" DELETE FROM perms WHERE name = %s AND perm = %s """, (name, perm)) + + def deluserstatus(self, name, status): + """ delete user with name """ + name = name.lower() + res = None + return self.db.execute(""" DELETE FROM statuses WHERE name = %s AND status = %s """, (name, status)) + + def deluserstatus(self, name, status): + """ delete user with name """ + name = name.lower() + res = None + return self.db.execute(""" DELETE FROM statuses WHERE name = %s AND status = %s """, (name, status)) + + def delete(self, name): + """ delete user with name """ + name = name.lower() + res = None + nr1 = self.db.execute(""" DELETE FROM userhosts WHERE name = %s """, name) + nr2 = self.db.execute(""" DELETE FROM perms WHERE name = %s """, name) + if nr1 and nr2: + res = 1 + return res + + def status(self, userhost, status): + """ check if user with has set """ + name = self.getname(userhost) + res = None + if name: + status = status.upper() + result = self.db.execute(""" SELECT status FROM statuses WHERE name = %s """, name) + if result: + for i in result: + if status == i[0]: + res = 1 + return res + + def gotperm(self, name, perm): + """ check if user had permission """ + name = name.lower() + perm = perm.upper() + result = self.db.execute(""" SELECT perm FROM perms WHERE name = %s """, name) + if result: + for i in result: + if i[0] == perm: + return True + + def getuserstatuses(self, name): + """ check if user had permission """ + name = name.lower() + return self.db.execute(""" SELECT status FROM statuses WHERE name = %s """, name) + + def getuseremail(self, name): + """ check if user had permission """ + name = name.lower() + return self.db.execute(""" SELECT email FROM email WHERE name = %s """, name) + + def getuserperms(self, name): + """ check if user had permission """ + name = name.lower() + return self.db.execute(""" SELECT perm FROM perms WHERE name = %s """, name) + + def getuserstatus(self, name): + """ check if user has status """ + name = name.lower() + status = status.upper() + return self.db.execute(""" SELECT status FROM statuses WHERE name = %s """, name) + + def gotstatus(self, name, status): + """ check if user has status """ + name = name.lower() + status = status.upper() + result = self.db.execute(""" SELECT status FROM statuses WHERE name = %s """, name) + if result: + for i in result: + if status == i[0]: + return True + + def gotuserhost(self, name, userhost): + """ check if user has userhost """ + name = name.lower() + result = self.db.execute(""" SELECT userhost FROM userhosts WHERE name = %s """, name) + if result: + for i in result: + if i[0] == userhost: + return True + + def getuserhosts(self, name): + """ check if user has userhost """ + name = name.lower() + return self.db.execute(""" SELECT userhost FROM userhosts WHERE name = %s """, name) + + gethosts = getuserhosts + + def gotpermit(self, name, permit): + """ check if user permits something """ + name = name.lower() + result = self.db.execute(""" SELECT permit FROM permits WHERE name = %s """, name) + if result: + for i in result: + if "%s %s" % permit == i[0]: + return True + + def getuserpermits(self, name): + """ check if user permits something """ + name = name.lower() + return self.db.execute(""" SELECT permit FROM permits WHERE name = %s """, name) + + def allowed(self, userhost, perms, log=True): + """ check if user with userhosts is allowed to execute perm command """ + if not type(perms) == types.ListType: + perms = [perms, ] + if 'ANY' in perms: + return 1 + res = None + name = self.getname(userhost) + if not name: + if log: + rlog(10, 'users', '%s userhost denied' % userhost) + return res + result = self.db.execute(""" SELECT perm FROM perms WHERE name = %s """, name) + if result: + for i in result: + if i[0] in perms: + res = 1 + if not res: + if log: + rlog(10, 'users', "%s perm %s denied" % (userhost, perms)) + return res + + def getemail(self, name): + """ get email of user """ + name = name.lower() + email = None + email = self.db.execute(""" SELECT email FROM email WHERE name = %s """, name) + if email: + return email[0][0] + + def setemail(self, name, email): + """ set email of user """ + res = 0 + try: + result = self.db.execute(""" INSERT INTO email(name, email) VALUES (%s, %s) """, (name, email)) + except: + try: + result = self.db.execute(""" UPDATE email SET email = %s WHERE name = %s """, (email, name)) + except: + pass + if result: + res = 1 + return res + + def getuserhosts(self, name): + """ check if user has userhost """ + name = name.lower() + return self.db.execute(""" SELECT userhost FROM userhosts WHERE name = %s """, name) + + + def addpermall(self, perm): + """ add permission to all users """ + perm = perm.upper() + for i in self.names(): + try: + self.addperm(i, perm) + except: + pass + + def delpermall(self, perm): + """ delete permission from all users """ + perm = perm.upper() + for i in self.names(): + try: + self.delperm(i, perm) + except: + pass + + def make_owner(self, userhosts): + + """ + see if owner already has a user account if not merge otherwise + add. + + :param userhosts: userhosts to inititalize owner with + :type userhosts: list or string + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.make_owner + + """ + + owner = [] + + if type(userhosts) != types.ListType: + owner.append(userhosts) + else: + owner = userhosts + + for userhost in owner: + username = self.getname(unicode(userhost)) + + if not username: + if not self.merge('owner', unicode(userhost)): + self.add('owner', [unicode(userhost), ], ['USER', 'OPER']) + elif username != 'owner': + self.delete(username) + if not self.merge('owner', unicode(userhost)): + self.add('owner', [unicode(userhost), ], ['USER', 'OPER']) + + def usersearch(self, name): + name = name.lower() + res = [] + for n in self.names(): + if name in n.lower(): res.append(n) + return res + \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/callbacks.py +++ gozerbot-0.99.1/build/lib/gozerbot/callbacks.py @@ -0,0 +1,274 @@ +# gozerbot/callbacks.py +# +# + +""" + bot callbacks .. callbacks occure on registered events. a precondition + function can optionaly be provided to see if the callback should fire. + callback can be executed in a Runner (1 thread that executes more jobs) + or in a seperate thread. callbacks are now unified for bot irc and jabber + events. +""" + +__copyright__ = 'this file is in the public domain' + +# IMPORT SECTION + +# gozerbot imports +from gozerbot.stats import stats +from gozerbot.threads.thr import getname +from config import config +from utils.log import rlog +from utils.exception import handle_exception +from utils.trace import calledfrom +from utils.generic import makeargrest +from utils.locking import lockdec +from utils.dol import Dol +from threads.thr import start_new_thread, getname +from runner import cbrunners + +# basic imports +import sys, copy, thread + +# END IMPORT + +# LOCK SECTION + +# locks +callbacklock = thread.allocate_lock() +locked = lockdec(callbacklock) + +# END LOCKS + +class Callback(object): + + """ + class representing a callback. + + :param func: function to execute + :param prereq: prerequisite function + :param plugname: plugin to register this callback with + :param kwargs: dict to pass on to the callback + :param threaded: whether the callback should be executed in its own thread + :param speed: determines which runnerspool to run this callback on + + """ + + def __init__(self, func, prereq, plugname, kwargs, threaded=False, \ +speed=5): + self.func = func # the callback function + self.prereq = prereq # pre condition function + self.plugname = plugname # plugin name + self.kwargs = kwargs # kwargs to pass on to function + self.threaded = copy.deepcopy(threaded) # run callback in thread + self.speed = copy.deepcopy(speed) # speed to execute callback with + self.activate = False + stats.up('callbacks', 'created') + +class Callbacks(object): + + """ + dict of lists containing callbacks. Callbacks object take care of + dispatching the callbacks based on incoming events. see Callbacks.check() + + """ + + def __init__(self): + + # self.cbs holds the dict of list. entry value is the event (string) + self.cbs = Dol() + + def size(self): + + """ return number of callbacks. """ + + return len(self.cbs) + + def add(self, what, func, prereq=None, kwargs=None, threaded=False, nr=False, speed=5): + + """ + add a callback. + + :param what: event to fire callback for + :param func: function to execute + :param prereq: prerequisite function + :param plugname: plugin to register this callback with + :param kwargs: dict to pass on to the callback + :param threaded: whether the callback should be executed in its own thread + :param speed: determines which runnerspool to run this callback on + + """ + + what = what.upper() + + # get the plugin this callback was registered from + plugname = calledfrom(sys._getframe(0)) + + # check if plugname is in loadlist .. if not don't add callback + if config['loadlist'] and not plugname in config['loadlist']: + rlog(-1, plugname, 'not in loadlist .. not adding callback') + return + + # see if kwargs is set if not init to {} + if not kwargs: + kwargs = {} + + # add callback to the dict of lists + if nr != False: + self.cbs.insert(nr, what, Callback(func, prereq, plugname, kwargs, threaded, speed)) + else: + self.cbs.add(what, Callback(func, prereq, plugname, kwargs, threaded, speed)) + + rlog(0, 'callbacks', 'added %s (%s)' % (what, plugname)) + + def unload(self, plugname): + + """ unload all callbacks registered in a plugin. """ + + unload = [] + + # look for all callbacks in a plugin + for name, cblist in self.cbs.iteritems(): + index = 0 + for item in cblist: + if item.plugname == plugname: + unload.append((name, index)) + index += 1 + + # delete callbacks + for callback in unload[::-1]: + self.cbs.delete(callback[0], callback[1]) + rlog(1, 'callbacks', 'unloaded %s' % callback[0]) + + def disable(self, plugname): + + """ disable all callbacks registered in a plugin. """ + + unload = [] + + # look for all callbacks in a plugin + for name, cblist in self.cbs.iteritems(): + index = 0 + for item in cblist: + if item.plugname == plugname: + item.activate = False + + def activate(self, plugname): + + """ activate all callbacks registered in a plugin. """ + + unload = [] + + # look for all callbacks in a plugin + for name, cblist in self.cbs.iteritems(): + index = 0 + for item in cblist: + if item.plugname == plugname: + item.activate = True + + def whereis(self, cmnd): + + """ show where ircevent.CMND callbacks are registered """ + + result = [] + cmnd = cmnd.upper() + + # locate callbacks for CMND + for c, callback in self.cbs.iteritems(): + if c == cmnd: + for item in callback: + if not item.plugname in result: + result.append(item.plugname) + + return result + + def list(self): + + """ show all callbacks. """ + + result = [] + + # loop over callbacks and collect callback functions + for cmnd, callbacks in self.cbs.iteritems(): + for cb in callbacks: + result.append(getname(cb.func)) + + return result + + def check(self, bot, ievent): + + """ + check for callbacks to be fired. + + :param bot: bot where event originates from + :param ievent: event that needs to be checked + + .. literalinclude:: ../../gozerbot/callbacks.py + :pyobject: Callbacks.check + + """ + + # check for "ALL" callbacks + if self.cbs.has_key('ALL'): + for cb in self.cbs['ALL']: + stats.up('callbacks', 'ALL') + self.callback(cb, bot, ievent) + + cmnd = ievent.cbtype or ievent.cmnd.upper() + + # check for CMND callbacks + if self.cbs.has_key(cmnd): + for cb in self.cbs[cmnd]: + stats.up('callbacks', cmnd) + self.callback(cb, bot, ievent) + + def callback(self, cb, bot, ievent): + + """ + do the actual callback with provided bot and ievent as arguments. + + :param cb: the callback to fire + :param bot: bot to call the callback on + :param ievent: the ievent that triggered the callback + + .. literalinclude:: ../../gozerbot/callbacks.py + :pyobject: Callbacks.callback + + """ + + try: + if not cb.activate: + return + # see if the callback pre requirement succeeds + if cb.prereq: + rlog(0, 'callbacks', 'excecuting in loop %s' % str(cb.prereq)) + if not cb.prereq(bot, ievent): + return + + # check if callback function is there + if not cb.func: + return + + # log and stats + rlog(0, 'callbacks', 'excecuting callback %s' % str(cb.func)) + stats.up('callbacks', getname(cb.func)) + stats.up('callbacks', cb.plugname) + + ievent.iscallback = True + + # launcn the callback .. either threaded or dispatched at runners + if cb.threaded: + start_new_thread(cb.func, (bot, ievent), cb.kwargs) + else: + cbrunners.put(cb.plugname, cb.func, bot, ievent, **cb.kwargs) + + except Exception, ex: + handle_exception() + +# INIT SECTION + +# callbacks object is the same for ICR and Jabber +callbacks = jcallbacks = Callbacks() +gn_callbacks = Callbacks() + +# END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/morphs.py +++ gozerbot-0.99.1/build/lib/gozerbot/morphs.py @@ -0,0 +1,159 @@ +# gozerbot/morphs.py +# +# + +""" convert input/output stream. """ + +## IMPORT SECTION + +# gozerbot imports +from utils.exception import handle_exception +from utils.trace import calledfrom + +# basic imports +import sys + +## END IMPORT + +## LOCK SECTION + +# no locks + +## END LOCK + +class Morph(object): + + """ + transform stream. + + :param func: morphing function + :type func: function + + """ + + def __init__(self, func): + self.plugname = calledfrom(sys._getframe(0)) + self.func = func + self.activate = False + + def do(self, *args, **kwargs): + + """ + do the morphing. + + .. literalinclude:: ../../gozerbot/morphs.py + :pyobject: Morph.do + """ + if not self.activate: + return + + try: + return self.func(*args, **kwargs) + except Exception, ex: + handle_exception() + +class MorphList(list): + + """ list of morphs. """ + + def add(self, func, index=None): + + """ + add morph. + + :param func: morphing function + :type func: function + :param index: index into the morphlist + :type index: integer + :rtype: self + + .. literalinclude:: ../../gozerbot/morphs.py + :pyobject: MorphList.add + + """ + + if not index: + self.append(Morph(func)) + else: + self.insert(index, Moprh(func)) + + return self + + def do(self, input, *args, **kwargs): + + """ + call morphing chain. + + :param input: data to do the morphing on + :type input: string + + .. literalinclude:: ../../gozerbot/morphs.py + :pyobject: MorphList.do + + """ + + for morph in self: + input = morph.do(input, *args, **kwargs) or input + + return input + + def unload(self, plugname): + + """ + unload morhps belonging to plug . + + :param plugname: the plugname to unload the morphs from + :type plugname: string + + .. literalinclude:: ../../gozerbot/morphs.py + :pyobject: MorphList.unload + + """ + + for index in range(len(self)-1, -1, -1): + if self[index].plugname == plugname: + del self[index] + + def disable(self, plugname): + + """ + disable morhps belonging to plug . + + :param plugname: the plugname to unload the morphs from + :type plugname: string + + .. literalinclude:: ../../gozerbot/morphs.py + :pyobject: MorphList.disable + + """ + + for index in range(len(self)-1, -1, -1): + if self[index].plugname == plugname: + self[index].activate = False + + def activate(self, plugname): + + """ + activate morhps belonging to plug . + + :param plugname: the plugname to unload the morphs from + :type plugname: string + + .. literalinclude:: ../../gozerbot/morphs.py + :pyobject: MorphList.activate + + """ + + for index in range(len(self)-1, -1, -1): + if self[index].plugname == plugname: + self[index].activate = False + +## INIT SECTION + +# moprhs used on input +inputmorphs = MorphList() + +# morphs used on output +outputmorphs = MorphList() + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/jsonusers.py +++ gozerbot-0.99.1/build/lib/gozerbot/jsonusers.py @@ -0,0 +1,751 @@ +# gozerbot/jsonusers.py +# +# + +""" bot's users in JSON file. NOT USED AT THE MOMENT. """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports +from gozerbot.datadir import datadir +from gozerbot.utils.log import rlog +from gozerbot.utils.generic import stripident, stripidents +from gozerbot.utils.exception import handle_exception, exceptionmsg +from gozerbot.utils.generic import die, stripped +from gozerbot.persist.persist import Persist +from gozerbot.utils.lazydict import LazyDict +from gozerbot.config import config + +# basic imports +import re, types, os, time + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + + +class JsonUser(Persist): + + """ LazyDict representing a user. """ + + def __init__(self, name="", userhosts=[], perms=[], permits=[], status=[], email=[]): + Persist.__init__(self, u"users" + os.sep + name) + self.data.name = self.data.name or name + self.data.userhosts = self.data.userhosts or userhosts + self.data.perms = self.data.perms or perms + self.data.permits = self.data.permits or permits + self.data.status = self.data.status or status + self.data.email = self.data.email or email + +class JsonUsers(Persist): + + """ class representing all users. """ + + def __init__(self, filename, ddir=None): + self.datadir = ddir or datadir + Persist.__init__(self, filename) + if not self.data: + self.data = LazyDict() + self.data.names = self.data.names or {} + + def all(self): + + """ get all users. """ + + result = [] + for name in self.data['names'].values(): + result.append(JsonUser(name)) + return result + + ### Misc. Functions + def size(self): + + """ return nr of users. """ + + return len(self.data['names']) + + def names(self): + + """ get names of all users. """ + + return self.data.names + + def byname(self, name): + """ return user by name. """ + try: + return JsonUser(name) + except KeyError: + return + + def merge(self, name, userhost): + """ add userhosts to user with name """ + user = self.byname(name) + if user: + user.userhosts.append(userhost) + self.save() + rlog(10, 'users', "%s merged with %s" % (userhost, name)) + return 1 + + def usersearch(self, userhost): + """ search for users with a userhost like the one specified """ + + result = [] + + for u, name in self.data.names.iteritems(): + + if userhost in u: + result.append((name, u)) + + return result + + def getuser(self, userhost): + try: + return JsonUser(self.data.names[userhost]) + except KeyError: + return + + ### Check functions + def exist(self, name): + """ see if user with exists """ + return self.byname(name) + + def allowed(self, userhost, perms, log=True): + """ check if user with userhosts is allowed to execute perm command """ + if not type(perms) == types.ListType: + perms = [perms, ] + if 'ANY' in perms: + return 1 + res = None + user = self.getuser(userhost) + if not user and log: + rlog(10, 'users', '%s userhost denied' % userhost) + return res + else: + uperms = set(user.perms) + sperms = set(perms) + intersection = sperms.intersection(uperms) + res = list(intersection) or None + if not res and log: + rlog(10, 'users', "%s perm %s denied" % (userhost, str(perms))) + return res + + def permitted(self, userhost, who, what): + """ check if (who,what) is in users permit list """ + user = self.getuser(userhost) + res = None + if user: + if '%s %s' % (who, what) in user.permits: + res = 1 + return res + + def status(self, userhost, status): + """ check if user with has set """ + user = self.getuser(userhost) + res = None + if user: + if status.upper() in user.statuses: + res = 1 + return res + + def gotuserhost(self, name, userhost): + """ check if user has userhost """ + user = self.byname(name) + return userhost in user.userhosts + + def gotperm(self, name, perm): + """ check if user had permission """ + user = self.byname(name) + if user: + return perm.upper() in user.perms + + def gotpermit(self, name, permit): + """ check if user permits something. permit is a (who, what) tuple """ + user = self.byname(name) + if user: + return '%s %s' % permit in user.permits + + def gotstatus(self, name, status): + """ check if user has status """ + user = self.byname(name) + return status.upper() in user.statuses + + ### Get Functions + def getname(self, userhost): + """ get name of user belonging to """ + user = self.getuser(userhost) + if user: + return user.name + + def gethosts(self, userhost): + """ return the userhosts of the user associated with the specified userhost """ + user = self.getuser(userhost) + if user: + return user.userhosts + + def getemail(self, userhost): + """ return the email of the specified userhost """ + user = self.getuser(userhost) + if user: + if user.email: + return user.email[0] + + def getperms(self, userhost): + """ return permission of user""" + user = self.getuser(userhost) + if user: + return user.perms + + def getpermits(self, userhost): + """ return permits of the specified userhost""" + user = self.getuser(userhost) + if user: + return user.permits + + def getstatuses(self, userhost): + + """ + return the list of statuses for the specified userhost. + + """ + + user = self.getuser(userhost) + if user: + return user.status + + def getuserhosts(self, name): + + """ + return the userhosts associated with the specified user. + + """ + + user = self.byname(name) + + if user: + return user.userhosts + + def getuseremail(self, name): + + """ + get email of user. + + """ + + user = self.byname(name) + + if user: + if user.email: + return user.email[0] + + def getuserperms(self, name): + + """ + return permission of user. + + """ + + user = self.byname(name) + if user: + return user.perms + + def getuserpermits(self, name): + + """ + return permits of user. + + """ + + user = self.byname(name) + + if user: + return user.permits + + def getuserstatuses(self, name): + + """ + return the list of statuses for the specified user. + + """ + + user = self.byname(name) + + if user: + return user.status + + def getpermusers(self, perm): + + """ + return all users that have the specified perm. + + """ + + result = [] + + for userdata in self.users.values(): + user = JsonUser(d=userdata) + + if perm.upper() in user.perms: + result.append(user.name) + + return result + + def getstatususers(self, status): + + """ + return all users that have the specified status. + + """ + + result = [] + + for userdata in self.users.values(): + user = JsonUser(d=userdata) + + if status in user.status: + result.append(user.name) + + return result + + ### Set Functions + def setemail(self, name, email): + + """ + set email of user. + + """ + + user = self.byname(name) + if user: + try: + user.email.remove(email) + except: + pass + user.email.insert(0, email) + self.save() + return True + return False + + ### Add functions + def add(self, name, userhosts, perms): + + """ + add an user. + + """ + + if type(userhosts) != types.ListType: + rlog(10, 'jsonusers', 'i need a list of userhosts') + return 0 + + if not os.path.isdir(self.datadir + os.sep + 'users'): + os.mkdir(self.datadir + os.sep + 'users') + + if not os.path.isdir(self.datadir + os.sep + 'users' + os.sep + name): + os.mkdir(self.datadir + os.sep + 'users' + os.sep + name) + + user = self.byname(name) + + if not user: + + try: + newuser = JsonUser(name=name) + newuser.userhosts.extend(userhosts) + newuser.perms.extend(perms) + self.users[name] = newuser + self.save() + except Exception, ex: + rlog(10, 'jsonusers', str(ex)) + return False + + rlog(10, 'users', '%s %s %s added to user database' % (name, \ +userhosts, perms)) + + return True + + def addemail(self, userhost, email): + + """ + add an email address to the userhost. + + """ + + user = self.getuser(userhost) + + if user: + user.email.append(email) + self.save() + rlog(10, 'users', '%s (%s) added to email' % (email, userhost)) + return 1 + + def addperm(self, userhost, perm): + + """ + add the specified perm to the userhost. + + """ + + user = self.getuser(userhost) + + if user: + user.perms.append(perm.upper()) + self.save() + rlog(10, 'users', '%s perm %s added' % (userhost, perm)) + return 1 + + def addpermit(self, userhost, permit): + + """ + add the given (who, what) permit to the given userhost. + + """ + + user = self.getuser(userhost) + + if user: + #p = '%s %s' % permit + user.permits.append(permit) + self.save() + rlog(10, 'users', '%s permit %s added' % (userhost, p)) + return 1 + + def addstatus(self, userhost, status): + + """ + add status to given userhost. + + """ + + user = self.getuser(userhost) + + if user: + user.status.append(status.upper()) + self.save() + rlog(10, 'users', '%s status %s added' % (name, status)) + return 1 + + def adduserhost(self, name, userhost): + + """ + add userhost. + + """ + + user = self.byname(name) + + if not user: + user = self.users[name] = User(name=name) + + user.userhosts.append(userhost) + self.save(user) + rlog(10, 'users', '%s (%s) added to userhosts' % (name, userhost)) + return 1 + + def adduseremail(self, name, email): + + """ + add email to specified user. + + """ + + user = self.byname(name) + + if user: + user.email.append(email) + self.save() + rlog(10, 'users', '%s email %s added' % (name, email)) + return 1 + + def adduserperm(self, name, perm): + + """ + add permission. + + """ + + user = self.byname(name) + + if user: + perm = perm.upper() + user.perms.append(perm) + self.save() + rlog(10, 'users', '%s perm %s added' % (name, perm)) + return 1 + + def adduserpermit(self, name, permit): + + """ + add (who, what) permit tuple to sepcified user. + + """ + + user = self.byname(name) + + if user: + p = '%s %s' % permit + user.permits.append(p) + self.save() + rlog(10, 'users', '%s permit %s added' % (name, p)) + return 1 + + def adduserstatus(self, name, status): + + """ + add status to given user. + + """ + + user = byname(name) + + if user: + user.status.append(status.upper()) + self.save() + rlog(10, 'users', '%s status %s added' % (name, status)) + return 1 + + def addpermall(self, perm): + + """ + add permission to all users. + + """ + + for user in self.users.values(): + user.perms.append(perm.upper()) + + self.save() + + ### Delete functions + + def delemail(self, userhost, email): + + """ + delete email from userhost. + + """ + + user = self.getuser(userhost) + + if user: + + if email in user.emails: + user.emails.remove(email) + self.save() + return 1 + + def delperm(self, userhost, perm): + + """ + delete perm from userhost. + + """ + + user = self.getuser(userhost) + if user: + p = perm.upper() + if p in user.perms: + user.perms.remove(p) + self.save() + return 1 + + def delpermit(self, userhost, permit): + + """ + delete permit from userhost. + + """ + + user = self.getuser(userhost) + + if user: + p = '%s %s' % permit + + if p in user.permits: + user.permits.remove(p) + self.save() + return 1 + + def delstatus(self, userhost, status): + + """ + delete status from userhost. + + """ + + user = self.getuser(userhost) + + if user: + st = status.upper() + + if st in user.status: + user.status.remove(st) + self.save() + return 1 + + def delete(self, name): + + """ + delete user with name. + + """ + + del self.users[name] + self.save() + return 1 + + def deluserhost(self, name, userhost): + + """ + delete the userhost entry. + + """ + + user = self.byname(name) + + if user: + + if userhost in user.userhosts: + user.userhosts.remove(userhost) + self.save() + rlog(10, 'users', '%s userhost %s deleted' % (name, userhost)) + return 1 + + def deluseremail(self, name, email): + + """ + delete email. + + """ + + user = self.byname(name) + + if user: + + if email in user.email: + user.email.remove(email) + self.save() + rlog(10, 'users', '%s email %s deleted' % (name, email)) + return 1 + + def deluserperm(self, name, perm): + + """ + delete permission. + + """ + + user = self.byname(name) + + if user: + p = perm.upper() + + if p in user.perms: + user.perms.remove(p) + self.save() + rlog(10, 'users', '%s perm %s deleted' % (name, p)) + return 1 + + def deluserpermit(self, name, permit): + + """ + delete permit. + + """ + + user = self.byname(name) + + if user: + p = '%s %s' % permit + + if p in user.permits: + user.permits.remove(p) + self.save() + rlog(10, 'users', '%s permit %s deleted' % (name, p)) + return 1 + + def deluserstatus(self, name, status): + + """ + delete the status from the given user. + + """ + + user = self.byname(name) + + if user: + st = status.upper() + + if st in user.status: + user.status.remove(status) + self.save() + rlog(10, 'users', '%s status %s deleted' % (name, st)) + return 1 + + def delallemail(self, name): + + """ + Delete all emails for the specified user. + + """ + + user = self.byname(name) + + if user: + user.email = [] + self.save() + rlog(10, 'users', '%s emails deleted' % (name, )) + return 1 + + def delpermall(self, perm): + + """ + delete permission from all users. + + """ + + for user in self.users.values(): + if user.name != 'owner': + del user.perms + + self.save() + return 1 + + def make_owner(self, userhosts): + + """ + see if owner already has a user account if not merge otherwise add. + + """ + + assert(userhosts) + owner = [] + + if type(userhosts) != types.ListType: + owner.append(userhosts) + else: + owner = userhosts + + for userhost in owner: + username = self.getname(unicode(userhost)) + + if not username: + + if not self.merge('owner', unicode(userhost)): + self.add('owner', [unicode(userhost), ], ['USER', 'OPER']) + +# ============ +# INIT SECTION + +# no vars + +# END INIT +# ======== \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/sausers.py +++ gozerbot-0.99.1/build/lib/gozerbot/sausers.py @@ -0,0 +1,1189 @@ +# gozerbot/users.py +# +# + +""" bot users """ + +__copyright__ = 'this file is in the public domain' + +## IMPORT SECTION + +# gozerbot imports +from gozerbot.datadir import datadir +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec +from gozerbot.utils.generic import stripident, stripidents +from gozerbot.utils.exception import handle_exception, exceptionmsg +from gozerbot.utils.generic import die, stripped +from gozerbot.utils.name import stripname +from gozerbot.persist.persist import Persist +from gozerbot.config import config +from gozerbot.jsonusers import JsonUsers + +# basic imports +import re, types, os, time, thread, sqlalchemy + +## END IMPORT + +## LOCK SECTION + +deletelock = thread.allocate_lock() +deletelocked = lockdec(deletelock) + +## END LOCK + +# see if we need to use the database or use the jsonusers + +if True: + if not config['nodb']: + from gozerbot.database.samodels import UserHost, User, Perms, Statuses + from gozerbot.database.alchemy import Session, query, getuser, byname, eagerload, dblocked + + else: + def trans(func, *args, **kwargs): + def transaction(*args, **kwargs): + func(*args, **kwargs) + return transaction + + +class DbUsers(object): + + """ + sqlalchemy backed users. + + """ + + def __init__(self, ddir=None): + self.datadir = ddir or datadir + + ### Misc. Functions + def size(self): + + """ + return nr of users. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.size + """ + + return int(query(User).count()) + + def names(self): + + """ + get names of all users. + + :rtype: list .. list of usernames + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.names + + """ + a = query(User).all() + return [n.name for n in a] + + @dblocked + def merge(self, name, userhost): + + """ + add userhosts to user with name. + + :param session: the session to act upon + :type session: sqllachemy.session.Session + :param name: name of the user to merge with + :type name: string + :param userhost: userhosts to merge + :type userhost: list .. list of userhosts + :rtype: boolean .. whether the merge succeeded + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.merge + + """ + + user = byname(name) + + if user: + + if name not in user.userhosts: + #Session.begin() + user.userhosts.append(userhost) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', "%s merged with %s" % (userhost, name)) + return True + + return False + + def usersearch(self, userhost): + + """ + search for users with a userhost like the one specified. + + :param userhost: userhost to search for + :type userhost: string + :rtype: list .. list of (user.name, user.userhost) tuples + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.usersearch + + """ + + n = query(UserHost).filter(UserHost.userhost.like('%%%s%%' % userhost)).all() + return [(u.name, u.userhost) for u in n] + + ### Check functions + def exist(self, name): + + """ + see if user with exists. + + :param name: name of the user + :rtype: boolean + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.exist + + """ + + a = byname(name) + + if a: + return True + + return False + + def allowed(self, userhost, perms, log=True): + + """ + check if user with userhosts is allowed to execute perm command. + + :param userhost: userhost to check + :type userhost: string + :param perms: the permissions to check + :type perms: list + :param log: whether the check should be logged + :type log: boolean + :rtype: list .. list of matching permissions or None + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.allowed + + """ + + if not type(perms) == types.ListType: + perms = [perms, ] + + if 'ANY' in perms: + return ['ANY', ] + + res = None + user = getuser(userhost) + + if not user: + + if log: + rlog(10, 'users', '%s userhost denied' % userhost) + return res + + else: + uperms = set(user.perms) + sperms = set(perms) + intersection = sperms.intersection(uperms) + res = list(intersection) or None + + if not res and log: + rlog(10, 'users', "%s perm %s denied" % (userhost, str(perms))) + + if res and log: + rlog(10, 'users', 'allowed %s %s perm' % (userhost, str(perms))) + + return res + + def permitted(self, userhost, who, what): + + """ + check if (who,what) is in users permit list. + + :param userhost: userhost to check permits for + :type userhost: string + :param who: who to permit + :type who: string + :param what: what to allow + :type what: string + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.permitted + """ + + user = getuser(userhost) + res = False + + if user: + if '%s %s' % (who, what) in user.permits: + res = True + + return res + + def status(self, userhost, status): + + """ + check if user with has set. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.status + + """ + + user = getuser(userhost) + res = False + + if user: + if status.upper() in user.statuses: + res = True + + return res + + def gotuserhost(self, name, userhost): + + """ + check if user has userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.gotuserhost + + """ + + user = byname(name) + + if user: + return userhost in user.userhosts + + return False + + def gotperm(self, name, perm): + + """ + check if user has permission. + + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.gotperm + """ + + user = byname(name) + + if user: + return perm.upper() in user.perms + + return False + + def gotpermit(self, name, permit): + + """ + check if user permits something. permit is a [who, what] list. + + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.gotpermit + + """ + + user = byname(name) + + if user: + return '%s %s' % permit in user.permits + + return "" + + def gotstatus(self, name, status): + + """ + check if user has status. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.gotstatus + + """ + + user = byname(name) + if user: + return status.upper() in user.statuses + + return False + + ### Get Functions + def getname(self, userhost): + + """ + get name of user belonging to . + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getname + + """ + + user = getuser(userhost) + + if user: + return str(user.name) + + return "" + + def gethosts(self, userhost): + + """ + return the userhosts of the user associated with the + specified userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.gethosts + """ + + user = getuser(userhost) + + if user: + return list(user.userhosts) + + return [] + + def getemail(self, userhost): + + """ + return the email of the specified userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getemail + """ + + user = getuser(userhost) + + if user: + if user.email: + return str(user.email[0]) + + return "" + + def getperms(self, userhost): + + """ + return permissions of user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getperms + + """ + + user = getuser(userhost) + + if user: + return list(user.perms) + + return [] + + def getpermits(self, userhost): + + """ + return permits of the specified userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getpermits + + """ + + user = getuser(userhost) + + if user: + return list(user.permits) + + return [] + + def getstatuses(self, userhost): + + """ + return the list of statuses for the specified userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getstatuses + + """ + + user = getuser(userhost) + + if user: + return list(user.statuses) + + def getuserhosts(self, name): + + """ + return the userhosts associated with the specified user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getuserhosts + """ + + user = byname(name) + + if user: + return list(user.userhosts) + + def getuseremail(self, name): + + """ + get email of user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getuseremail + + """ + + user = byname(name) + + if user: + if user.email: + return str(user.email[0]) + + def getuserperms(self, name): + + """ + return permission of user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getuserperms + """ + + user = byname(name) + + if user: + return list(user.perms) + + def getuserpermits(self, name): + + """ + return permits of user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getuserpermits + """ + + user = byname(name) + + if user: + return list(user.permits) + + def getuserstatuses(self, name): + + """ + return the list of statuses for the specified user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getuserstatuses + """ + + user = byname(name) + + if user: + return list(user.statuses) + + def getpermusers(self, perm): + + """ + return all users that have the specified perm. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getpermusers + + """ + n = query(Perms).filter(Perms.perm==perm.upper()).all() + return [user.name for user in n] + + def getstatususers(self, status): + + """ + return all users that have the specified status. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.getstatususers + + """ + + n = query(Statuses).filter(Statuses.status==status.upper()).all() + return [user.name for user in n] + + ### Set Functions + + @dblocked + def setemail(self, name, email): + + """ + set email of user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.setemail + """ + + user = byname(name) + + if user: + #Session.begin() + try: + user.email.remove(email) + except: + pass + user.email.insert(0, email) + #Session.refresh(user) + #Session.commit() + #Session.close() + return True + + return False + + ### Add functions + + @dblocked + def add(self, name, userhosts, perms): + + """ + add an user. session argument is provided by @trans + + :param name: name of the user + :type name: string + :param userhosts: userhosts to add + :type userhosts: list .. list of userhosts + :param perms: the permissions to add + :type perms: list .. list of permissions + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.add + + """ + + if type(userhosts) != types.ListType: + rlog(10, 'users', 'i need a list of userhosts') + return False + + name = stripname(name.lower()) + + if not os.path.isdir(self.datadir + os.sep + 'users'): + os.mkdir(self.datadir + os.sep + 'users') + + if not os.path.isdir(self.datadir + os.sep + 'users' + os.sep + name): + os.mkdir(self.datadir + os.sep + 'users' + os.sep + name) + + user = byname(name) + + if not user: + + try: + #Session.begin() + newuser = User(name=name) + newuser.userhosts.extend(userhosts) + newuser.perms.extend(perms) + Session.add(newuser) + #Session.commit() + #Session.close() + except sqlalchemy.exc.IntegrityError, ex: + if 'not unique' in str(ex): + rlog(10, 'users', '%s %s already exists' % (name, userhosts)) + Session.close() + return True + else: + raise + except Exception, ex: + raise + + rlog(10, 'users', '%s %s %s added to user database' % (name, userhosts, perms)) + + return True + + @dblocked + def addemail(self, userhost, email): + + """ + add an email address to the userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.addemail + + """ + + user = getuser(userhost) + + if user: + #Session.begin() + user.email.append(email) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s (%s) added to email' % (email, userhost)) + return True + + return False + + @dblocked + def addperm(self, userhost, perm): + + """ + add the specified perm to the userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.addperm + + """ + + user = getuser(userhost) + + if user: + #Session.begin() + user.perms.append(perm.upper()) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s perm %s added' % (userhost, perm)) + return True + + return False + + @dblocked + def addpermit(self, userhost, permit): + + """ + add the given [who, what] permit to the given userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.addpermit + + """ + + user = getuser(userhost) + + if user: + p = '%s %s' % permit + #Session.begin() + user.permits.append(p) + Session.refesh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s permit %s added' % (userhost, p)) + return True + + return False + + @dblocked + def addstatus(self, userhost, status): + + """ + add status to given userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.addstatus + + """ + + user = getuser(userhost) + + if user: + #Session.begin() + user.statuses.append(status.upper()) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', '%s status %s added' % (name, status)) + return True + + return False + + @dblocked + def adduserhost(self, name, userhost): + + """ + add userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.adduserhost + + """ + + user = byname(name) + #Session.begin() + if not user: + user = User(name=name) + Session.add(user) + + user.userhosts.append(userhost) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', '%s (%s) added to userhosts' % (name, userhost)) + return True + + @dblocked + def adduseremail(self, name, email): + + """ + add email to specified user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.adduseremail + + """ + + user = byname(name) + + if user: + #Session.begin() + user.email.append(email) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', '%s email %s added' % (name, email)) + return True + + @dblocked + def adduserperm(self, name, perm): + + """ + add permission. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.adduserperm + + """ + + user = byname(name) + + if user: + #Session.begin() + perm = perm.upper() + user.perms.append(perm) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', '%s perm %s added' % (name, perm)) + return True + + return False + + @dblocked + def adduserpermit(self, name, who, permit): + + """ + add (who, what) permit tuple to specified user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.adduserpermit + + """ + + user = byname(name) + + if user: + p = '%s %s' % (who, permit) + #Session.begin() + user.permits.append(p) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', '%s permit %s added' % (name, p)) + return True + + return False + + @dblocked + def adduserstatus(self, name, status): + + """ + add status to given user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.adduserstatus + + """ + + user = byname(name) + + if user: + #Session.begin() + user.statuses.append(status.upper()) + #Session.commit() + #Session.refresh(user) + #Session.close() + rlog(10, 'users', '%s status %s added' % (name, status)) + return True + + return False + + @dblocked + def addpermall(self, perm): + + """ + add permission to all users. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.addpermall + """ + + users = query(User).all() + + if users: + #Session.begin() + for user in users: + user.perms.append(perm.upper()) + #Session.refresh(user) + #Session.commit() + #Session.close() + + ### Delete functions + + @dblocked + def delemail(self, userhost, email): + + """ + delete email from userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delemail + + """ + + user = getuser(userhost) + + if user: + if email in user.emails: + #Session.begin() + user.emails.remove(email) + #Session.commit() + #Session.refresh(user) + #Session.close() + return True + + return False + + @dblocked + def delperm(self, userhost, perm): + + """ + delete perm from userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delperm + + """ + + user = getuser(userhost) + + if user: + p = perm.upper() + + if p in user.perms: + #Session.begin() + user.perms.remove(p) + #Session.commit() + #Session.refresh(user) + #Session.close() + return True + + return False + + @dblocked + def delpermit(self, userhost, permit): + + """ + delete permit from userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delpermit + """ + + user = getuser(userhost) + + if user: + p = '%s %s' % permit + + if p in user.permits: + #Session.begin() + user.permits.remove(p) + #Session.commit() + #Session.refresh(user) + #Session.close() + return True + + return False + + @dblocked + def delstatus(self, userhost, status): + + """ + delete status from userhost. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delstatus + + """ + + user = getuser(userhost) + + if user: + st = status.upper() + + if st in user.statuses: + #Session.begin() + user.statuses.remove(st) + #Session.commit() + #Session.refresh(user) + #Session.close() + return True + + return False + + @dblocked + def delete(self, name): + + """ + delete user with name. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delete + + """ + name = stripname(name) + user = byname(name) + + if user: + rlog(10, 'users', "deleting %s" % name) + #Session.begin() + Session.delete(user) + #Session.commit() + #Session.close() + return True + + return False + + @dblocked + def deluserhost(self, name, userhost): + + """ + delete the userhost entry. + + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.deluserhost + """ + + user = byname(name) + + if user: + if userhost in user.userhosts: + #Session.begin() + user.userhosts.remove(userhost) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s userhost %s deleted' % (name, userhost)) + return True + + return False + + @dblocked + def deluseremail(self, name, email): + + """ + delete email. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.deluseremail + + """ + + user = byname(name) + + if user: + if email in user.email: + #Session.begin() + user.email.remove(email) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s email %s deleted' % (name, email)) + return True + + return False + + @dblocked + def deluserperm(self, name, perm): + + """ + delete permission. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.deluserperm + + """ + + user = byname(name) + + if user: + p = perm.upper() + + if p in user.perms: + #Session.begin() + user.perms.remove(p) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s perm %s deleted' % (name, p)) + return True + + return False + + @dblocked + def deluserpermit(self, name, permit): + + """ + delete permit. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.deluserpermit + + """ + + user = byname(name) + + if user: + p = '%s %s' % permit + + if p in user.permits: + #Session.begin() + user.permits.remove(p) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s permit %s deleted' % (name, p)) + return True + + return False + + @dblocked + def deluserstatus(self, name, status): + + """ + delete the status from the given user. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.deluserstatus + + """ + + user = byname(name) + + if user: + st = status.upper() + + if st in user.statuses: + #Session.begin() + user.statuses.remove(status) + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s status %s deleted' % (name, st)) + return True + + return False + + @dblocked + def delallemail(self, name): + + """ + Delete all emails of the specified user. + + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delallemail + + """ + + user = byname(name) + + if user: + #Session.begin() + user.email = [] + #Session.refresh(user) + #Session.commit() + #Session.close() + rlog(10, 'users', '%s emails deleted' % (name, )) + return True + + return False + + @dblocked + def delpermall(self, perm): + + """ + delete permission from all users. + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.delpermall + + """ + + users = query(User).options(eagerload('perms')).all() + + for user in users: + + if user.name != 'owner': + + try: + #Session.begin() + user.perms.remove(perm) + #Session.refresh(user) + #Session.commit() + #Session.close() + except ValueError: + pass + + return True + + def make_owner(self, userhosts): + + """ + see if owner already has a user account if not merge otherwise + add. + + :param userhosts: userhosts to inititalize owner with + :type userhosts: list or string + + .. literalinclude:: ../../gozerbot/users.py + :pyobject: DbUsers.make_owner + + """ + + owner = [] + + if type(userhosts) != types.ListType: + owner.append(userhosts) + else: + owner = userhosts + + for userhost in owner: + username = self.getname(unicode(userhost)) + + if not username: + if not self.merge('owner', unicode(userhost)): + self.add('owner', [unicode(userhost), ], ['USER', 'OPER']) + elif username != 'owner': + self.delete(username) + if not self.merge('owner', unicode(userhost)): + self.add('owner', [unicode(userhost), ], ['USER', 'OPER']) + +## INIT SECTION + +# default to database + +if not config['nodb']: + users = DbUsers() +else: + # otheriwse use json users + users = JsonUsers(datadir + os.sep + (config['jsonuser'] or 'users.json')) + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/periodical.py +++ gozerbot-0.99.1/build/lib/gozerbot/periodical.py @@ -0,0 +1,567 @@ +# gozerbot/periodical.py +# +# + +__author__ = "Wijnand 'tehmaze' Modderman - http://tehmaze.com" +__license__ = "BSD License" + +## IMPORT SECTION + +# gozerbot imports + +from utils.exception import handle_exception +from utils.trace import calledfrom, whichmodule +from utils.locking import lockdec +from utils.log import rlog +from utils.timeutils import strtotime +import threads.thr as thr + +# basic imorts +import datetime, sys, time, thread, types + +## END IMPORT + +## LOCK SECTION +# locks and vars +plock = thread.allocate_lock() +locked = lockdec(plock) +pidcount = 0 + +# END LOCK + +class JobError(Exception): + + """ + job error exception. + + """ + + pass + +class Job(object): + + """ + job to be scheduled. + + """ + + group = '' + pid = -1 + + def __init__(self): + global pidcount + pidcount += 1 + self.pid = pidcount + + def id(self): + + """ + return job id. + + """ + + return self.pid + + def member(self, group): + + """ + check for group membership. + + :param group: group to check for + :type group: string + :rtype: boolean + + """ + + return self.group == group + + def do(self): + # try the callback + try: + self.func(*self.args, **self.kw) + except Exception, ex: + handle_exception() + +class JobAt(Job): + + """ + job to run at a specific time/interval/repeat. + + :param start: start time + :type start: int, float or string + :param interval: time between alarms + :type interval: integer + :param repeat: number of repeats + :type interval: integer + :param func: the function to execute + :type func: function + + """ + + def __init__(self, start, interval, repeat, func, *args, **kw): + Job.__init__(self) + self.func = func + self.args = args + self.kw = kw + self.repeat = repeat + self.description = "" + self.counts = 0 + + # check start time format + if type(start) in [types.IntType, types.FloatType]: + self.next = float(start) + elif type(start) in [types.StringType, types.UnicodeType]: + d = strtotime(start) + if d and d > time.time(): + self.next = d + else: + raise JobError("invalid date/time") + + if type(interval) in [types.IntType]: + d = datetime.timedelta(days=interval) + self.delta = d.seconds + else: + self.delta = interval + + def __repr__(self): + + """ + return a string representation of the JobAt object. + + """ + + return '' % (str(self.next), + str(self.delta), self.repeat, str(self.func)) + + def check(self): + + """ + run check to see if job needs to be scheduled. + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: JobAt.check + + """ + + if self.next <= time.time(): + rlog(0, 'periodical', 'running %s - %s' % (str(self.func), self.description)) + self.func(*self.args, **self.kw) + self.next += self.delta + self.counts += 1 + if self.repeat > 0 and self.counts >= self.repeat: + return False # remove this job + return True + +class JobInterval(Job): + + """ + job to be scheduled at certain interval. + + :param interval: time between alarms + :type interval: integer + :param repeat: number of repeats + :type interval: integer + :param func: the function to execute + :type func: function + + """ + + def __init__(self, interval, repeat, func, *args, **kw): + Job.__init__(self) + self.func = func + self.args = args + self.kw = kw + self.repeat = int(repeat) + self.counts = 0 + self.interval = float(interval) + self.description = "" + self.next = time.time() + self.interval + self.group = None + rlog(-10, 'periodical', 'scheduled next run of %s in %d seconds' % \ +(str(self.func), self.interval)) + + def __repr__(self): + return '' % (str(self.next), str(self.interval), self.repeat, self.group, +str(self.func)) + + def check(self): + + """ + run check to see if job needs to be scheduled. + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: JobInterval.check + """ + + if self.next <= time.time(): + rlog(0, 'periodical', 'running %s - %s' % (str(self.func), self.description)) + self.next = time.time() + self.interval + thr.start_new_thread(self.do, ()) + self.counts += 1 + if self.repeat > 0 and self.counts >= self.repeat: + return False # remove this job + return True + + +class Periodical(object): + + """ + periodical scheduler. + + """ + + SLEEPTIME = 1 # smallest interval possible + + def __init__(self): + self.jobs = [] + self.running = [] + self.run = True + + def start(self): + + """ + start the periodical scheduler. + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.start + """ + + thr.start_new_thread(self.checkloop, ()) + + def addjob(self, sleeptime, repeat, function, description="" , *args, **kw): + + """ + add a periodical job. + + :param sleeptime: sleeptime between intervals + :type sleeptime: float + :param repeat: number of times to repeat + :type repeat: integer + :param function: function to execute + :type function: function + :param description: description of the periodical job + :type description: string + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.addjob + + """ + + job = JobInterval(sleeptime, repeat, function, *args, **kw) + job.group = calledfrom(sys._getframe()) + job.description = str(description) or whichmodule() + self.jobs.append(job) + return job.pid + + def changeinterval(self, pid, interval): + + """ + change interval of of peridical job. + + :param pid: id op the periodical job + :type pid: integer + :param interval: interval to set + :type interval: integer + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.changeinterval + """ + + for i in periodical.jobs: + + if i.pid == pid: + i.interval = interval + i.next = time.time() + interval + + def looponce(self): + for job in self.jobs: + + if job.next <= time.time(): + self.runjob(job) + + def checkloop(self): + + """ + main loop of the periodical scheduler. + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.checkloop + + """ + + while self.run: + + for job in self.jobs: + + if job.next <= time.time(): + self.runjob(job) + + time.sleep(self.SLEEPTIME) + + def runjob(self, job): + + """ + run a periodical job + + :param job: the job to be runned + :type job: Job + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.runjob + + """ + + if not job.check(): + self.killjob(job.id()) + else: + self.running.append(job) + + def kill(self): + + ''' + kill all jobs invoked by another module. + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.kill + ''' + + group = calledfrom(sys._getframe()) + self.killgroup(group) + + def killgroup(self, group): + + ''' + kill all jobs with the same group. + + :param group: the group of jobs to kill + :type group: string + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.killgroup + + ''' + + def shoot(): + + """ knock down all jobs belonging to group. """ + + deljobs = [job for job in self.jobs if job.member(group)] + + for job in deljobs: + self.jobs.remove(job) + + try: + self.running.remove(job) + except ValueError: + pass + + rlog(5, 'periodical', 'killed %d jobs for %s' % (len(deljobs), \ +group)) + + del deljobs + + shoot() # *pow* you're dead ;) + + def killjob(self, jobId): + + ''' + kill one job by its id. + + :param jobId: the id of the job to kill + :type jobId: integer + :rtype: integer .. number of jobs killed + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: Periodical.killjob + ''' + + def shoot(): + deljobs = [x for x in self.jobs if x.id() == jobId] + numjobs = len(deljobs) + for job in deljobs: + self.jobs.remove(job) + try: + self.running.remove(job) + except ValueError: + pass + del deljobs + return numjobs + + return shoot() # *pow* you're dead ;) + + +def interval(sleeptime, repeat=0): + + """ + interval decorator. + + :param sleeptime: time to sleep + :type sleeptime: integer + :param repeat: number of times to repeat the job + :type repeat: integet + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: interval + + """ + + group = calledfrom(sys._getframe()) + + def decorator(function): + decorator.func_dict = function.func_dict + + def wrapper(*args, **kw): + job = JobInterval(sleeptime, repeat, function, *args, **kw) + job.group = group + job.description = whichmodule() + periodical.jobs.append(job) + rlog(-15, 'periodical', 'new interval job %d with sleeptime %d' % \ +(job.id(), sleeptime)) + + return wrapper + + return decorator + +def at(start, interval=1, repeat=1): + + """ + at decorator. + + :param start: start time of the periodical job + :type start: integer, float or string + :param interval: time between jobs + :type sleeptime: integer + :param repeat: number of times to repeat the job + :type repeat: integet + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: at + + """ + + group = calledfrom(sys._getframe()) + + def decorator(function): + decorator.func_dict = function.func_dict + + def wrapper(*args, **kw): + job = JobAt(start, interval, repeat, function, *args, **kw) + job.group = group + job.description = whichmodule() + periodical.jobs.append(job) + + wrapper.func_dict = function.func_dict + return wrapper + + return decorator + +def persecond(function): + + """ + per second decorator. + + :param function: function to execute every second + :type function: function + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: persecond + """ + + minutely.func_dict = function.func_dict + group = calledfrom(sys._getframe()) + + def wrapper(*args, **kw): + job = JobInterval(1, 0, function, *args, **kw) + job.group = group + job.description = whichmodule() + periodical.jobs.append(job) + rlog(-15, 'periodical', 'new interval job %d running per second' % \ +job.id()) + + return wrapper + +def minutely(function): + + """ + minute decorator. + + :param function: function to execute every minute + :type function: function + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: minutely + + """ + + minutely.func_dict = function.func_dict + group = calledfrom(sys._getframe()) + + def wrapper(*args, **kw): + job = JobInterval(60, 0, function, *args, **kw) + job.group = group + job.description = whichmodule() + periodical.jobs.append(job) + rlog(-15, 'periodical', 'new interval job %d running minutely' % \ +job.id()) + + return wrapper + +def hourly(function): + + """ + hour decorator. + + :param function: function to execute every hour + :type function: function + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: hourly + + """ + + rlog(-15, 'periodical', '@hourly(%s)' % str(function)) + hourly.func_dict = function.func_dict + group = calledfrom(sys._getframe()) + + def wrapper(*args, **kw): + job = JobInterval(3600, 0, function, *args, **kw) + job.group = group + job.description = whichmodule() + rlog(-15, 'periodical', 'new interval job %d running hourly' % job.id()) + periodical.jobs.append(job) + + return wrapper + +def daily(function): + + """ + day decorator. + + :param function: function to execute every hour + :type function: function + + .. literalinclude:: ../../gozerbot/periodical.py + :pyobject: daily + + """ + + rlog(-15, 'periodical', '@daily(%s)' % str(function)) + daily.func_dict = function.func_dict + group = calledfrom(sys._getframe()) + + def wrapper(*args, **kw): + job = JobInterval(86400, 0, function, *args, **kw) + job.group = group + job.description = whichmodule() + periodical.jobs.append(job) + rlog(-15, 'periodical', 'new interval job %d running daily' % job.id()) + + return wrapper + +## INIT SECTION + +# the periodical scheduler +periodical = Periodical() + +## END INIT \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/exit.py +++ gozerbot-0.99.1/build/lib/gozerbot/exit.py @@ -0,0 +1,73 @@ +# gozerbot/exit.py +# +# + +""" gozerbot's finaliser """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + + +# gozerbot imports +from utils.log import rlog +from utils.trace import whichmodule +from eventhandler import mainhandler +from plugins import plugins +from fleet import fleet +from persist.persist import saving +from runner import runners_stop +from gozerbot.config import config + +# basic imports +import atexit, os, time, sys + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +def globalshutdown(): + + """ + shutdown the bot. + + .. literalinclude:: ../../gozerbot/exit.py + :pyobject: globalshutdown + + """ + + rlog(10, 'GOZERBOT', 'SHUTTING DOWN') + + try: + os.remove('gozerbot.pid') + except: + pass + + try: + runners_stop() + rlog(10, 'gozerbot', 'shutting down fleet') + fleet.exit() + rlog(10, 'gozerbot', 'shutting down plugins') + plugins.exit() + rlog(10, 'GOZERBOT', 'done') + os._exit(0) + + except Exception, ex: + rlog(10, 'gozerbot.exit', 'exit error %s:' % str(ex)) + +# ============ +# INIT SECTION + +# register shutdown function +#atexit.register(globalshutdown) + +# END INIT +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/channels.py +++ gozerbot-0.99.1/build/lib/gozerbot/channels.py @@ -0,0 +1,136 @@ +# gozerbot/channels.py +# +# + +""" + channel related data. implemented with a persisted dict of dicts. + + :example: + + key = channels[event.channel]['key'] + +""" + +__copyright__ = 'this file is in the public domain' + +# IMPORT SECTION +# gozerbot imports +from persist.pdod import Pdod + +# END IMPORT + +class Channels(Pdod): + + """ + channels class .. per channel data. + + :param fname: filename to persist the data to + :type fname: string + + """ + + def __init__(self, fname): + + # call base constructor + Pdod.__init__(self, fname) + + # make sure attributes are initialised + for j in self.data.values(): + if not j.has_key('perms'): + j['perms'] = [] + if not j.has_key('autovoice'): + j['autovoice'] = 0 + + def __setitem__(self, a, b): + + # if item is not in dict initialise it to empty dict + if not self.data.has_key(a): + self.data[a] = {} + + # assign data + self.data[a] = b + + def getchannels(self): + + """ + return channels. + + .. literalinclude:: ../../gozerbot/channels.py + :pyobject: Channels.getchannels + + """ + + result = [] # list of channels found + + # loop over channels + for channel in self.data.keys(): + channel = channel.strip() + if channel not in result: + result.append(channel) + + return result + + def getchannelswithkeys(self): + + """ + return channels with keys. + + .. literalinclude:: ../../gozerbot/channels.py + :pyobject: Channels.getchannelswithkeys + + """ + + result = [] + + # loop over channels gathering channel name and key + for channel in self.data.keys(): + channel = channel.strip() + try: + key = self.data[channel]['key'] + if not channel + ' ' + key in result: + result.append(channel + ' ' + key) + except KeyError: + if channel not in result: + result.append(channel) + + return result + + def getkey(self, channel): + + """ + return key of channel if set. + + :param channel: channel to get key from + :type channel: string + + .. literalinclude:: ../../gozerbot/channels.py + :pyobject: Channels.getkey + + """ + + try: + key = self.data[channel]['key'] + except: + key = None + + return key + + def getnick(self, channel): + + """ + return bot nick of channel if set. + + :param channel: channel to get key from + :type channel: string + + .. literalinclude:: ../../gozerbot/channels.py + :pyobject: Channels.getnick + + """ + + try: + nick = self.data[channel]['nick'] + except: + nick = None + + return nick --- gozerbot-0.99.1.orig/build/lib/gozerbot/tests.py +++ gozerbot-0.99.1/build/lib/gozerbot/tests.py @@ -0,0 +1,326 @@ +# gozerbot/tests.py +# +# + +""" gozerbot tests framework. """ + +## IMPORT SECTION + +# gozerbot imports +from config import config +from threads.thr import start_new_thread +from utils.locking import lockdec +from utils.log import rlog +from utils.trace import calledfrom, whichmodule +from utils.exception import exceptionmsg +from utils.dol import Dol +from eventbase import EventBase + +# basic imports +import sys, re, thread, copy, time, threading, random + +## END IMPORT + +# used to copy attributes +cpy = copy.deepcopy + +## LOCK SECTION + +# locks +testlock = threading.RLock() +locked = lockdec(testlock) + +## END LOCK + +class Test(object): + + """ a test object. """ + + def __init__(self, execstring="", expect="", descr="", where="", fakein=""): + self.plugin = calledfrom(sys._getframe(1)) + if not self.plugin: + self.plugin = calledfrom(sys._getframe(2)) + self.descr = cpy(descr) + self.execstring = cpy(execstring) + self.expect = cpy(expect) + self.response = "" + self.groups = [] + self.error = "" + self.where = cpy(where) + self.fakein = cpy(fakein) + self.start = None + self.end = None + self.prev = None + self.activate = False + + def __str__(self): + return "test %s (%s) %s ==> %s (%s)" % (self.descr, self.where, self.execstring, self.response, self.error) + + def begin(self): + if self.start: + self.start() + return self + + def run(self, bot, event): + + """ run the test on bot with event. """ + + if not self.activate: + return + + if config['loadlist'] and self.plugin not in config['loadlist']: + return + + mevent = copy.deepcopy(event) + mevent.onlyqueues = False + mevent.channel = '#dunkbots' + bot.userhosts['bottest'] = 'bottest@test' + bot.userhosts['mtest'] = 'mekker@test' + bot.userhosts['exec'] = 'exec@gozerbot' + if 'exec@gozerbot' not in bot.state['joinedchannels']: + bot.state['joinedchannels'].append('exec@gozerbot') + bot.channels['exec@gozerbot'] = {} + if '#dunkbots' not in bot.state['joinedchannels']: + bot.state['joinedchannels'].append('#dunkbots') + bot.channels['#dunkbots'] = {'cc': '!'} + self.error = "" + self.response = "" + self.groups = [] + origexec = self.execstring + origexpect = self.expect + if self.start: + self.start() + return self + if self.end: + self.end() + return self + if self.fakein: + bot.fakein(self.fakein) + return self + if self.prev and self.prev.groups: + try: + execstring = self.execstring % self.prev.groups + self.execstring = execstring + except TypeError: + pass + try: + expect = self.expect % self.prev.groups + self.expect = expect + except TypeError: + pass + + self.execstring = self.execstring.replace('{{ me }}', mevent.nick) + mevent.txt = mevent.origtxt = str(self.execstring) + rlog(100, 'tests', 'launching %s' % mevent.txt) + + from gozerbot.plugins import plugins + self.response = plugins.cmnd(bot, mevent) + + if self.response and self.expect: + self.expect = self.expect.replace('{{ me }}', mevent.nick) + expects = self.expect.split('|') + got = False + for expect in expects: + regex = re.compile(expect) + result = regex.search(str(self.response)) + + if result: + got = True + break + + if not got: + self.error = 'invalid response' + else: + self.groups = result.groups() + + self.execstring = origexec + self.expect = origexpect + + return self + +class Tests(object): + + """ collection of all tests. """ + + def __init__(self): + self.tests = [] + self.err = Dol() + self.toolate = Dol() + self.teller = 0 + + #@locked + def add(self, execstr, expect=None, descr="", fakein=""): + + """ add a test. """ + + where = whichmodule(1) + if not where: + where = whichmodule(2) + if not where: + where = whichmodule(3) + test = Test(execstr, expect, descr, where, fakein) + self.tests.append(test) + return self + + #@locked + def fakein(self, execstr, expect=None, descr=""): + + """ call bot.fakein(). """ + + where = whichmodule(1) + if not where: + where = whichmodule(2) + if not where: + where = whichmodule(3) + test = Test(execstr, expect, descr, where, execstr) + test.where = where + self.tests.append(test) + return self + + def start(self, func): + + """ optional start function. """ + where = whichmodule(1) + if not where: + where = whichmodule(2) + if not where: + where = whichmodule(3) + test = Test() + test.start = func + test.where = where + test.execstring = 'start' + self.tests.append(test) + return self + + def end(self, func): + + """ optional end function. """ + + where = whichmodule(1) + if not where: + where = whichmodule(2) + if not where: + where = whichmodule(3) + test = Test() + test.end = func + test.where = where + test.execstring = 'end' + self.tests.append(test) + return self + + def unload(self, plugname): + + """ unload tests. """ + + for i in range(len(self.tests)-1, -1, -1): + if self.tests[i].plugin == plugname: + del self.tests[i] + + return self + + def activate(self, plugname): + + """ activate tests. """ + + for i in range(len(self.tests)-1, -1, -1): + if self.tests[i].plugin == plugname: + self.tests[i].activate = True + + return self + + def disable(self, plugname): + + """ unload tests. """ + + for i in range(len(self.tests)-1, -1, -1): + if self.tests[i].plugin == plugname: + self.tests[i].activate = False + + return self + + def dorun(self, bot, event, tests, where, plug=None): + teller = 0 + err = {} + toolate = [] + prev = None + for test in tests: + if event.rest and event.rest not in test.plugin: + continue + if prev: + test.prev = prev + prev = test + if test.expect: + teller += 1 + try: + starttime = time.time() + self.teller += 1 + e = copy.deepcopy(event) + result = test.run(bot, e) + finished = time.time() + if finished - starttime > 10: + self.toolate[test.execstring] = finished - starttime + if not result: + continue + if not result.error: + event.reply("OK %s (%s) ==> %s" % (test.execstring, test.where, result.response)) + else: + self.err[test.execstring] = test + event.reply('ERROR %s (%s): %s ==> %s (%s)' % (test.error, test.where, test.execstring, test.response, test.expect)) + except Exception, ex: + test.error = exceptionmsg() + self.err[test.execstring] = test + event.reply(test.error) + + return self + + def dotests(self, bot, event, threaded=False, plug=None): + + """ fire all tests. """ + + #event = EventBase(eventin) + groups = Dol() + + for test in self.tests: + groups.add(test.where, test) + + threads = [] + testlist = [] + + for where, tests in groups.iteritems(): + testlist.append((where, tests)) + + random.shuffle(testlist) + + for t in testlist: + where, tests = t + if plug and plug not in where: + continue + event.reply("running tests on %s" % where) + if threaded: + thread = start_new_thread(self.dorun, (bot, event, tests, where, plug)) + threads.append(thread) + else: + self.dorun(bot, event, tests, where, plug) + + for thread in threads: + thread.join() + + event.done() + + def sleep(self, seconds): + + """ sleep nr of seconds. """ + + time.sleep(seconds) + return self + +## INIT SECTION + +# expect is for examples + +expect = {} + +# the tests +tests = Tests() + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/__init__.py +++ gozerbot-0.99.1/build/lib/gozerbot/__init__.py @@ -0,0 +1,45 @@ +# gozerbot package +# +# + +""" gozerbot core package. """ + +__copyright__ = 'this file is in the public domain' + +__version__ = "0.99.1" + +__all__ = ['aliases', 'database', 'ignore', 'periodical', 'tests', \ +'botbase', 'datadir', 'persist', 'threads', \ +'cache', 'eventbase', 'irc', 'plughelp', 'users', \ +'callbacks', 'eventhandler', 'jabber', 'plugins', 'utils', \ +'channels', 'examples', 'jsonusers', 'plugs', 'wait', \ +'commands', 'exit', 'less', 'redispatcher', \ +'compat', 'fleet', 'monitor', 'rest', \ +'config', 'morphs', 'runner', 'wave', \ +'contrib', 'gozerimport', 'partyline', 'stats', 'utils', 'reboot', 'xmpp'] + +# ============== +# IMPORT SECTION + +from eggs import loadegg +import os + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +# ============ +# INIT SECTION + +loadegg('simplejson', [os.getcwd(), os.getcwd() + os.sep + 'gozernest'], log=False) +loadegg('sqlalchemy', [os.getcwd(), os.getcwd() + os.sep + 'gozernest'], log=False) + +# END INIT +# ======== \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/commands.py +++ gozerbot-0.99.1/build/lib/gozerbot/commands.py @@ -0,0 +1,579 @@ +# gozerbot/commands.py +# +# + +""" + + This module implements the commands a user can give. It also contains + the global cmnds object to which all commands are added. + + example: + + :: + + cmnds.add('hello', handle_hello, 'USER') + + + :var cmnds: global commands object + +""" + +__copyright__ = 'this file is in the public domain' + +# IMPORT SECTION + +# gozerbot imports +from gozerbot.stats import stats +from utils.generic import makeoptions +from eventbase import defaultevent +from config import config +from utils.log import rlog +from utils.trace import calledfrom +from utils.exception import handle_exception +from utils.locking import lockdec +from runner import cmndrunners +from threads.thr import start_new_thread, start_bot_command + +# basic imports +import sys, re, copy, types, thread + +# END IMPORT + +# LOCK SECTION + +# command lock +commandlock = thread.allocate_lock() +locked = lockdec(commandlock) + +# END LOCK + +class Command(object): + + """ + implements a command. + + :param func: the function to call when a commands gets triggered + :param perm: permissions the command needs before it gets fired + :type perm: list + :param plugname: the plugin this commands is implemented in + :type param: string + :param threaded: determines if the command has to be run in its own thread + :type threaded: False or True + :param allowqueue: determines if output of this commands can be used in pipeline queues + :type threaded: False or True + :param options: options for this command + :type options: dict + + """ + + def __init__(self, func, perm, plugname, speed=5, threaded=False, allowqueue=True, options={'--speed': 5, '--chan': '', '--filter': '', '--how': 'msg'}): + + self.name = str(func) # function name is string representation of the function + self.func = func # function to call + + # make sure permission(s) are stored in a list + if type(perm) == types.ListType: + self.perms = list(perm) + else: + self.perms = [perm, ] + + self.plugname = plugname # plugin name + self.speed = copy.deepcopy(speed) # speed to execute command with + self.threaded = copy.deepcopy(threaded) # set if threaded exec is required + self.allowqueue = copy.deepcopy(allowqueue) # set if command is allowed to be used in pipeline + self.options = dict(options) # options set in the command + self.activate = True + +class Commands(dict): + + """ + the commands object is a dict containing the commands. dict key is the + command (1 word). + + """ + + def __setitem__(self, name, value): + + """ set command. """ + + dict.__setitem__(self, name, value) + + def __delitem__(self, name): + + """ delete command. """ + + dict.__delitem__(self, name) + + def size(self): + + """ + nr of commands. + + :rtype: integer + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.size + + """ + + return len(self) + + def activate(self, plugname): + cp = dict(self) + for i in cp.values(): + if i.plugname == plugname: + i.activate = True + + def disable(self, plugname): + cp = dict(self) + for i in cp.values(): + if i.plugname == plugname: + i.activate = False + + def whatperms(self): + + """ + return all possible permissions. + + :rtype: list + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.whatperms + + """ + + result = [] + + # loop over the commands and collect all possible permissions + cp = dict(self) + for i in cp.values(): + for j in i.perms: + if j not in result: + result.append(j) + + return result + + def list(self, perm): + + """ + list commands with permission perm. + + :param perm: the permission + :type perm: string + :rtype: list + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.list + + """ + + result = [] + + # make sure perm is a list + if type(perm) != types.ListType: + perm = perm.upper() + perms = [perm, ] + else: + perms = perm + + # loop over commands collecting all command having permission + cp = dict(self) + for name, cmnd in cp.items(): + for i in perms: + if i in cmnd.perms: + result.append(name) + + return result + + def getfuncnames(self, plug): + + """ + get all function names of commands in a plugin. + + :param plug: plugname + :type plug: string + :rtype: list + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.getfuncnames + + """ + + result = [] + + # collect function names + cp = dict(self) + for i in cp.values(): + if i.plugname == plug: + result.append(i.func.func_name) + + return result + + def getoptions(self, command): + + """ + get options of a command. + + :param command: the command to get options for + :type command: string + :rtype: dict + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.getoptions + + """ + + cp = dict(self) + for name, cmnd in cp.iteritems(): + if name == command: + return makeoptions(defaultevent, cmnd.options) + + #@locked + def permoverload(self, name, perms): + + """ + overload permission of function with funcname. + + :param name: name of command to overload permission of + :type name: string + :param perms: permission to overload the command with + :type perms: list + :rtype: boolean + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.permoverload + + """ + + # make sure all perms are uppercase + perms = [perm.upper() for perm in perms] + + # overload the command with given permissions + for com in self.values(): + try: + if com.func.func_name == name: + com.perms = perms + return True + except AttributeError: + rlog(10, 'commands', "permoverload: no %s function" % name) + return False + + #@locked + def add(self, cmnd, func, perm, speed=5, threaded=False, allowqueue=True, options={'--speed': 5, '--chan': '', '--filter': '', '--how': 'msg'}): + + """ + add a command. + + :param func: the function to call when a commands gets triggered + :param perm: permissions the command needs before it gets fired + :type perm: list + :param plugname: the plugin this commands is implemented in + :type param: string + :param threaded: determines if the command has to be run in its own thread + :type threaded: boolean + :param allowqueue: determines if output of this commands can be used in pipeline queues + :type threaded: boolean + :param options: options for this command + :type options: dict + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.add + + """ + + # plugin where the command is added + plugname = calledfrom(sys._getframe(0)) + # check if plugin is in loadlist .. if not dont register command. + if config['loadlist'] and plugname not in config['loadlist']: + rlog(1, 'commands', 'NOT LOADING %s' % plugname) + return + + rlog(-3, 'commands', 'added %s (%s) ' % (cmnd, plugname)) + + # add command + self[cmnd.lower()] = Command(func, perm, plugname, speed, threaded, allowqueue, options) + self[cmnd.lower()].name = cmnd.lower() + + def apropos(self, txt, perms=[]): + + """ + search for command. + + :param txt: txt to search commands with + :type txt: string + :param perms: contain permission that must match first + :type perms: list + :rtype: list + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.apropos + + """ + + result = [] + + # loop over commands collecting all commands that contain given txt + cp = dict(self) + for name, cmnd in cp.iteritems(): + if perms: + go = False + for i in perms: + if i in cmnd.perms: + go = True + if not go: + continue + if re.search(txt, name): + result.append(name) + + return result + + #@locked + def unload(self, plugname): + + """ + unload plugin commands. + + :param plugname: name of the plugin that needs to be unloaded + :type plugname: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.unload + + """ + + results = [] + + # look for the commands registerd in plugin + for name, cmnd in self.iteritems(): + if cmnd.plugname == plugname: + results.append(name) + + got = False + + # remove commands + for name in results: + + try: + del self[name] + rlog(-3, 'commands', 'unloaded %s (%s)' % (name, plugname)) + got = True + except KeyError: + got = False + + if got: + return True + + def whereis(self, command): + + """ + locate plugin a command is registered in. + + :param command: command to search for + :type command: string + :rtype: list + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.whereis + + """ + + result = [] + + # find plugin + cp = dict(self) + for name, cmnd in cp.iteritems(): + if name == command: + if not cmnd.plugname in result: + result.append(cmnd.plugname) + + return result + + def perms(self, name): + + """ + get permission of command. + + :param command: command to lookup permissions for + :type command: string + :rtype: list + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.perms + + """ + + name = name.lower() + + if self.has_key(name): + return self[name].perms + else: + return [] + + #@locked + def setperm(self, command, perm): + + """ + set permission of command. + + :param command: command to set permission of + :type command: string + :param perm: the permission + :type perm: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.setperm + + """ + + command = command.lower() + perm = perm.upper() + + if self.has_key(command): + if perm not in self[command].perms: + self[command].perms.append(perm) + return True + return False + + def getcommand(self, txt): + + """ + return command matching txt. + + :param txt: txt to match commands against + :type txt: string + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.getcommand + + """ + + textlist = txt.split() + + if not textlist: + return None + + cmnd = textlist[0].lower() + if self.has_key(cmnd): + com = self[cmnd] # the command + return com + else: + return None + + def options(self, command): + + """ + return options dict of command. + + :param command: the command to get the options of + :type command: string + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.options + + """ + + try: + return self[command].options + except KeyError: + return + + def getcommands(self, plugin): + + """ + get all commands of a plugin. + + :param plugin: the plugin to get commands of + :type plugin: string + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.getcommands + + """ + + result = [] + + tmp = dict(self) + for name, cmnd in tmp.iteritems(): + if cmnd.plugname == plugin: + result.append(name) + + return result + + #@locked + def dispatch(self, com, txt, wait=False): + + """ + dispatch command. + + :param com: the command object to dispatch with + :type com: Command + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Commands.dispatch + + """ + + if com.threaded: + thread = start_new_thread(com.func, (txt, )) + if wait: + thread.join() + else: + cmndrunners[10-com.speed].put(com.name, com.func, txt) + return 1 + +class Botcommands(Commands): + + """ + commands for the bot .. dispatch with (bot, ircevent) as arguments. + + """ + + #@locked + def dispatch(self, com, bot, ievent, wait=False): + + """ + dispatch on event passing bot and ievent as arguments. + + :param com: the command object + :type com: gozerbot.commands.Command + :param bot: the bot this command is given on + :type bot: gozerbot.botbase.BotBase + :param ievent: the event triggering this command + :rtype: boolean + + .. literalinclude:: ../../gozerbot/commands.py + :pyobject: Botcommands.dispatch + + """ + + if not com.activate: + return False + + if bot.stopped: + return False + + #ievent = copy.deepcopy(ieventin) + + # stats + stats.up('cmnds', com.name) + stats.up('cmnds', com.plugname) + stats.up('cmnds', 'speed%s' % com.speed) + + # execute command + if com.threaded or ievent.threaded: + thread = start_bot_command(com.func, (bot, ievent)) + #if thread and wait: + # thread.join() + else: + speed = ievent.speed or com.speed + ievent.speed = speed + cmndrunners.put(com.name, com.func, bot, ievent) + return True + +# INIT SECTION + +cmnds = Botcommands() + +# END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/eventhandler.py +++ gozerbot-0.99.1/build/lib/gozerbot/eventhandler.py @@ -0,0 +1,192 @@ +# gozerbot/eventhandler.py +# +# + +""" + event handler. use to dispatch function in main loop. + +""" + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports +from utils.exception import handle_exception +from utils.log import rlog +from utils.locking import lockdec +from threads.thr import start_new_thread + +# basic imports +import Queue, thread + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# locks +handlerlock = thread.allocate_lock() +locked = lockdec(handlerlock) + +# END LOCK +# ======== + +## START + +class Eventhandler(object): + + """ + events are handled in 11 queues with different priorities: + queue0 is tried first queue10 last. + + """ + + def __init__(self): + self.sortedlist = [] + self.queues = {} + + for i in range(11): + self.queues[i] = Queue.Queue() + self.sortedlist.append(i) + + self.sortedlist.sort() + self.go = Queue.Queue() + self.stopped = False + self.running = False + self.nooutput = False + + def start(self): + + """ + start the eventhandler thread. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.start + + """ + + self.stopped = False + + if not self.running: + start_new_thread(self.handleloop, ()) + self.running = True + + def stop(self): + + """ + stop the eventhandler thread. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.stop + + """ + + self.running = False + self.stopped = True + self.go.put('Yihaaa') + + def put(self, speed, func, *args, **kwargs): + + """ + put item on the queue. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.put + + """ + + self.queues[10-speed].put_nowait((func, args, kwargs)) + self.go.put('go') + + def getready(self): + + """ + check queues from available functions to execute. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.getready + + """ + + ready = [] + + for i in self.sortedlist: + if self.queues[i].qsize(): + ready.append(i) + break + + return ready + + def handle_one(self): + + """ + do 1 loop over ready queues. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.handle_one + + """ + + ready = self.getready() + + for i in ready: + self.dispatch(self.queues[i]) + + def handleloop(self): + + """ + thread that polls the queues for items to dispatch. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.handleloop + + """ + + rlog(0, 'eventhandler', 'starting handle thread') + + while not self.stopped: + self.go.get() + self.handle_one() + + rlog(0, 'eventhandler', 'stopping %s' % str(self)) + + def dispatch(self, queue): + + """ + dispatch functions from provided queue. + + .. literalinclude:: ../../gozerbot/eventhandler.py + :pyobject: Eventhandler.dispatch + + """ + + + try: + todo = queue.get_nowait() + except Queue.Empty: + return + + try: + (func, args, kwargs) = todo + func(*args, **kwargs) + except ValueError: + try: + (func, args) = todo + func(*args) + except ValueError: + (func, ) = todo + func() + + except: + handle_exception() + +# INIT SECTION + +# handler to use in main prog +mainhandler = Eventhandler() + + +# END INIT \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/runner.py +++ gozerbot-0.99.1/build/lib/gozerbot/runner.py @@ -0,0 +1,172 @@ +# jsb/runner.py +# +# + +""" threads management to run jobs. """ + +## jsb imports + +from gozerbot.threads.thr import getname, start_new_thread, start_bot_command +from gozerbot.utils.generic import handle_exception +from gozerbot.utils.trace import callstack +from gozerbot.threads.threadloop import RunnerLoop +from gozerbot.utils.log import rlog + +## basic imports + +import Queue +import time +import thread +import random +import logging +import sys + +## Runner class + +class Runner(RunnerLoop): + + """ + a runner is a thread with a queue on which jobs can be pushed. + jobs scheduled should not take too long since only one job can + be executed in a Runner at the same time. + + """ + + def __init__(self, name="runner", doready=True): + RunnerLoop.__init__(self, name) + self.working = False + self.starttime = time.time() + self.elapsed = self.starttime + self.finished = time.time() + self.doready = doready + + def handle(self, descr, func, *args, **kwargs): + """ schedule a job. """ + self.working = True + name = getname(str(func)) + try: + #rlockmanager.acquire(getname(str(func))) + name = getname(str(func)) + self.name = name + rlog(10, "runner", 'running %s: %s' % (descr, name)) + self.starttime = time.time() + func(*args, **kwargs) + self.finished = time.time() + self.elapsed = self.finished - self.starttime + #if self.elapsed > 3: + #logging.debug('ALERT %s %s job taking too long: %s seconds' % (descr, str(func), self.elapsed)) + except Exception, ex: handle_exception() + #finally: rlockmanager.release() + self.working = False + +## BotEventRunner class + +class BotEventRunner(Runner): + + def handle(self, descr, func, bot, ievent, *args, **kwargs): + """ schedule a bot command. """ + try: + self.starttime = time.time() + #lockmanager.acquire(getname(str(func))) + name = getname(str(func)) + self.name = name + self.working = True + rlog(10, "runner", "now running %s" % name) + func(bot, ievent, *args, **kwargs) + + for queue in ievent.queues: + queue.put_nowait(None) + + self.finished = time.time() + self.elapsed = self.finished - self.starttime + #if self.elapsed > 3: + # logging.info('ALERT %s %s job taking too long: %s seconds' % (descr, str(func), self.elapsed)) + #if ievent.iscommand: ievent.ready() + #if not ievent.type == "OUTPUT" and not ievent.dontclose: ievent.ready() + #time.sleep(0.001) + except Exception, ex: + handle_exception(ievent) + #finally: lockmanager.release(getname(str(func))) + self.working = False + self.name = "finished" + +## Runners class + +class Runners(object): + + """ runners is a collection of runner objects. """ + + def __init__(self, max=100, runnertype=Runner, doready=True): + self.max = max + self.runners = [] + self.runnertype = runnertype + self.doready = doready + + def runnersizes(self): + """ return sizes of runner objects. """ + result = [] + for runner in self.runners: result.append("%s - %s" % (runner.queue.qsize(), runner.name)) + return result + + def stop(self): + """ stop runners. """ + for runner in self.runners: runner.stop() + + def start(self): + """ overload this if needed. """ + pass + + def put(self, *data): + """ put a job on a free runner. """ + #logging.debug("size is %s" % len(self.runners)) + for runner in self.runners: + if not runner.queue.qsize(): + runner.put(*data) + return + runner = self.makenew() + runner.put(*data) + + def running(self): + """ return list of running jobs. """ + result = [] + for runner in self.runners: + if runner.queue.qsize(): result.append(runner.nowrunning) + return result + + def makenew(self): + """ create a new runner. """ + runner = None + for i in self.runners: + if not i.queue.qsize(): return i + if len(self.runners) < self.max: + runner = self.runnertype(self.doready) + runner.start() + self.runners.append(runner) + else: runner = random.choice(self.runners) + return runner + + def cleanup(self): + """ clean up idle runners. """ + #if not len(self.runners): logging.debug("nothing to clean") + for index in range(len(self.runners)-1, -1, -1): + runner = self.runners[index] + #logging.debug("cleanup %s" % runner.name) + if not runner.queue.qsize(): + try: runner.stop() ; del self.runners[index] + except IndexError: pass + except: handle_exception() + #else: logging.info("now running: %s" % runner.nowrunning) + +## global runners + +cmndrunners = defaultrunner = longrunner = Runners(50, BotEventRunner) +cbrunners = Runners(100, BotEventRunner, doready=False) +waitrunners = Runners(10, Runner) + +## runners_start + +def runners_start(): + pass + +def runners_stop(): + pass --- gozerbot-0.99.1.orig/build/lib/gozerbot/config.py +++ gozerbot-0.99.1/build/lib/gozerbot/config.py @@ -0,0 +1,425 @@ +# gozerbot/config.py +# +# + +""" + gozerbot config module. + + :var config: default config object + +""" + +__copyright__ = 'this file is in the public domain' + +# IMPORT SECTION + +# gozerbot imports +from utils.exception import handle_exception +from utils.log import rlog + +# simplejson imports + +from simplejson import loads, dumps + +# basic imports +import os, types, thread, logging + +# END IMPORT + +class Config(dict): + + """ + config class is a dict containing json strings. is writable to file + and human editable. + + :param ddir: datadir where the config file lives + :type ddir: string + :param filename: filename of the config file + :type filename: string + :param inittxt: initial txt to fill the config file with + :type inittxt: string + + """ + + def __init__(self, ddir=None, filename=None, inittxt=None, verbose=False, *args, **kw): + dict.__init__(self, *args, **kw) + self.jsondb = None + self.dir = ddir or 'gozerdata' + self.filename = filename or 'config' + self.cfile = self.dir + os.sep + self.filename + + if hasattr(os, 'mkdir'): + # check if provided dir exists if not create it + if not os.path.exists(self.dir): + os.mkdir(self.dir) + + # see if initial data has to be written + if inittxt and not os.path.exists(self.cfile): + self.write_init(inittxt) + + self.configlist = [] + self.lock = thread.allocate_lock() + self.load(verbose) + + def __getitem__(self, item): + if not self.has_key(item): + return None + else: + return dict.__getitem__(self, item) + + def set(self, item, value): + dict.__setitem__(self, item, value) + self.save() + + def load(self, verbose=False): + + """ + load the config file. + + .. literalinclude:: ../../gozerbot/config.py + :pyobject: Config.load + + """ + + self.reload() + + if self.filename == 'mainconfig': + self.setdefault('owner', []) + self.setdefault('loglevel', 10) + self.setdefault('loglist', []) + self.setdefault('resource', 'gozerbot') + self.setdefault('quitmsg', "http://gozerbot.googlecode.com") + self.setdefault('mask', "700") + self.setdefault('debug', 0) + self.setdefault('nodb', 0) + self.setdefault('plugdeny', []) + self.setdefault('db_driver', 'olddb') + self.setdefault('dbtype', "sqlite") + self.setdefault('dbname', "db/main.db") + self.setdefault('dbhost', "localhost") + self.setdefault('dbuser', "bart") + self.setdefault('dbpasswd', "mekker2") + self.setdefault('loadlist', ["alias", "all", "at", "chanperm", "choice", "code", "core", "count", "fleet", "googletalk", "grep", "ignore", "irc", "jabber", "job", "misc", "nickcapture", "nickserv", "not", "quote", "reload", "rest", "reverse", "size", "sort", "tail", "tell", "to", "underauth", "uniq", "user", "userstate", "stats", "throttle"]) + self.setdefault('dotchars', " .. ") + self.setdefault('floodallow', 1) + self.setdefault('auto_register', 0) + + elif 'fleet' in self.dir: + self.setdefault("enable", 1) + self.setdefault("type", "irc") + self.setdefault("owner", []) + self.setdefault("user", "") + self.setdefault("nick", "gozerbot") + self.setdefault("server", "") + self.setdefault("host", "") + self.setdefault("password", "") + self.setdefault("port", 0) + self.setdefault("ssl", 0) + self.setdefault("ipv6", 0) + self.setdefault("username", "gozerbot") + self.setdefault("realname", "GOZERBOT") + self.setdefault("defaultcc", "!") + self.setdefault("quitmsg", "http://gozerbot.googlecode.com") + self.setdefault("bindhost", "") + self.setdefault("nolimiter", 0) + self.setdefault("nickservpass", "") + self.setdefault("nickservtxt", ["set unfiltered on", ]) + self.setdefault("auto_register", 0) + self.setdefault("stripident", 0) + self.setdefault("loadlist", []) + + import gozerbot + self['version'] = "GOZERBOT %s " % gozerbot.__version__ + self['version'] += "RELEASE" + + if verbose: + rlog(10, 'config', str(self)) + + def save(self): + + """ + save the config file. + + :rtype: integer .. number of lines saved + + .. literalinclude:: ../../gozerbot/config.py + :pyobject: Config.save + + + """ + + written = [] + curitem = None + + try: + + self.lock.acquire() + + # read existing config file if available + try: + self.configlist = open(self.cfile, 'r').readlines() + except IOError: + self.configlist = [] + + # make temp file + configtmp = open(self.cfile + '.tmp', 'w') + teller = 0 + + # write header if not already there + if not self.configlist: + configtmp.write('# %s\n\n' % self.cfile) + + # loop over original lines replacing updated data + for line in self.configlist: + teller += 1 + + # skip comment + if line.startswith('#'): + configtmp.write(line) + continue + + # take part after the = + try: + keyword = line.split('=')[0].strip() + curitem = keyword + except IndexError: + configtmp.write(line) + continue + + # write JSON string of data + if self.has_key(keyword): + configtmp.write('%s = %s\n' % (keyword, dumps(self[keyword]))) + written.append(keyword) + else: + configtmp.write(line) + + # write data not found in original config file + for keyword, value in self.iteritems(): + if keyword in written: + continue + curitem = keyword + configtmp.write('%s = %s\n' % (keyword, dumps(value))) + + # move temp file to original + configtmp.close() + try: + os.rename(self.cfile + '.tmp', self.cfile) + except WindowsError: + # no atomic operation supported on windows! error is thrown when destination exists + os.remove(self.cfile) + os.rename(self.cfile + '.tmp', self.cfile) + self.lock.release() + return teller + + except Exception, ex: + print "ERROR WRITING %s CONFIG FILE: %s .. %s" % (self.cfile, str(ex), curitem) + try: + self.lock.release() + except: + pass + return + + def reload(self): + + """ + reload the config file. + + .. literalinclude:: ../../gozerbot/config.py + :pyobject: Config.reload + + + """ + + curline = "" + + # read file and set config values to loaded JSON entries + try: + + # open file + f = open(self.cfile, 'r') + + # loop over data in config file + for line in f: + curline = line + line = line.strip() + if not line or line.startswith('#'): + continue + else: + key, value = line.split('=', 1) + self[key.strip()] = loads(unicode(value.strip())) + + except IOError: + pass + except Exception, ex: + print "ERROR READING %s CONFIG FILE: %s .. %s" % (self.cfile, str(ex), curline) + + def write_init(self, txt): + + """ + write initial version of the config file .. mainconfig or fleet + config. + + :param txt: txt to write to config file + :type txt: string + + .. literalinclude:: ../../gozerbot/config.py + :pyobject: Config.write_init + + + """ + if hasattr(os, 'mkdir'): + # check if datadir is there .. if not create it + if not os.path.isdir(self.dir): + os.mkdir(self.dir) + + # check if config file is already there .. if not init it + if not os.path.isfile(self.cfile): + cfgfile = open(self.cfile, 'w') + cfgfile.write(txt) + cfgfile.close() + + +mainconfigtxt = """# gozerbot config +# +# +# TAKE NOTE THAT FORMAT IS IN JSON NOW SO USE " not ' + +# GLOBAL OWNER + +# example: owner = ["~bart@127.0.0.1"] (include ident if needed) +owner = [] + +# logging level +loglevel = 10 + +# list of plugins to log +loglist = [] + +# quit message +quitmsg = "http://gozerbot.googlecode.com" + +# botterdata dir mask +mask = "700" + +# enable debugging +debug = 0 + +# database stuff + +nodb = 0 +db_driver = "olddb" +dbtype = "sqlite" +dbname = "db/main.db" +dbhost = "localhost" +dbuser = "bart" +dbpasswd = "mekker2" + +# json backstore + +#jsonuser = "users.json" + +# loadlist +loadlist = ["alias", "all", "at", "chanperm", "choice", "code", "core", "count", "fleet", "googletalk", "grep", "ignore", "irc", "jabber", "job", "misc", "nickcapture", "nickserv", "not", "quote", "reload", "rest", "reverse", "size", "sort", "tail", "tell", "to", "underauth", "uniq", "user", "userstate", "plug", "test", "stats", "inform", "admin", "throttle", "mysqlkeepalive", "gozernet"] + +# chars used to seperate result +dotchars = " .. " + +# nr of lines before flood protect kicks in + +floodallow = 2 + +# auto register new users + +auto_register = 0 + +# resource used by xmpp bot (global) +resource = "gozerbot" + +""" + + +""" init txt for fleet config """ + + +fleetbotconfigtxt = """# gozerbot fleet bot config +# +# +# TAKE NOTE THAT FORMAT IS IN JSON NOW SO USE " not ' + +## MAIN STUFF + +# set to 0 to disable +enable = 1 + +# set type to jabber or irc +type = "irc" + +# owner (irc/jabber) +owner = [] + +## CONNECTION STUFF + +# user (jabber) .. needs to be full JID because server is taken from it +user = "botter@jsonbot.org" + +# nick (irc) +nick = "gozerbot" + +# server (irc) +server = "" + +# host (jabber) .. server to connect to +host = "" + +# password (irc and jabber) +password = "" + +# port (irc and jabber) .. 0 uses default value +port = 0 + +# ssl (irc) +ssl = 0 + +# use ipv6 (irc) +ipv6 = 0 + +# bots username (irc) +username = "gozerbot" + +# bots realname (irc) +realname = "GOZERBOT" + +## OTHER STUFF + +# default control character +defaultcc = "!" + +# quit message +quitmsg = "http://gozerbot.googlecode.com" + +# bindhost .. uncomment to enable +bindhost = "" + +# disable limiter (irc) +nolimiter = 0 + +# nickserv .. set pass to enable nickserv ident (irc) +nickservpass = "" +nickservtxt = ["set unfiltered on"] + +# allow every command except OPER commands +auto_register = 0 + +# strip ident string +stripident = 0 + +loadlist = [] + +""" + +# INIT SECTION + +# main config object +config = Config(filename='mainconfig', inittxt=mainconfigtxt) + +# END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/examples.py +++ gozerbot-0.99.1/build/lib/gozerbot/examples.py @@ -0,0 +1,113 @@ +# gozerbot/examples.py +# +# + +""" + examples is a dict of example objects. + +""" + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + + +# basic imports +import re + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +class Example(object): + + """ + an example. + + :param descr: description of the example + :type descr: string + :param ex: the example + :type ex: string + + """ + + def __init__(self, descr, ex): + self.descr = descr + self.example = ex + +class Examples(dict): + + """ + examples object is a dict. + + """ + + def add(self, name, descr, ex): + + """ + add description and example. + + :param name: name of the example + :type name: string + :param descr: description of the example + :type descr: string + :param ex: the example + :type ex: string + + .. literalinclude:: ../../gozerbot/examples.py + :pyobject: Examples.add + + """ + + self[name.lower()] = Example(descr, ex) + + def size(self): + + """ + return size of examples dict. + + :rtype: integer + + .. literalinclude:: ../../gozerbot/examples.py + :pyobject: Examples.size + + """ + + return len(self.keys()) + + def getexamples(self): + + """ + get all examples in list. + + :rtype: list + + .. literalinclude:: ../../gozerbot/examples.py + :pyobject: Examples.getexamples + """ + + result = [] + for i in self.values(): + ex = i.example.lower() + exampleslist = re.split('\d\)', ex) + for example in exampleslist: + if example: + result.append(example.strip()) + return result + +# ============ +# INIT SECTION + +# main examples object +examples = Examples() + +# END INIT +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/aliases.py +++ gozerbot-0.99.1/build/lib/gozerbot/aliases.py @@ -0,0 +1,135 @@ +# gozerbot/aliases.py +# +# + +""" + command aliases. this module contains the aliases of commands. aliases + are stored in the gozerdata/aliases.new json file. + +""" + +__copyright__ = 'this file is in the public domain' + +### IMPORT SECTION + +# gozerbot imports +from gozerbot.persist.persist import Persist +from gozerbot.datadir import datadir +from gozerbot.utils.locking import lockdec +# basic import +import os, thread + +### END IMPORT + +## LOCK SECTION + +aliaslock = thread.allocate_lock() +aliaslocked = lockdec(aliaslock) + +#@aliaslocked +def aliasreverse(what): + + """ get the reverse of an alias. + + :param what: alias to get command for + :type what: string + + .. literalinclude:: ../../gozerbot/aliases.py + :pyobject: aliasreverse + """ + + for i, j in aliases.data.iteritems(): + if what == j: + return i + +#@aliaslocked +def aliascheck(ievent): + + """ check if alias is available. + + :param ievent: event to check for aliases + + .. literalinclude:: ../../gozerbot/aliases.py + :pyobject: aliascheck + """ + + try: + + cmnd = ievent.txt.split()[0] + alias = aliases.data[cmnd] + ievent.txt = ievent.txt.replace(cmnd, alias, 1) + ievent.alias = alias + ievent.aliased = cmnd + + except (IndexError, KeyError): + pass + +@aliaslocked +def aliassave(): + + """ save aliases to json file. + + .. literalinclude:: ../../gozerbot/aliases.py + :pyobject: aliassave + """ + + aliases.save() + +@aliaslocked +def aliasset(fromm, to): + + """ set an alias. + + :param from: alias to set + :type from: string + :param to: command to alias + :type to: string + + .. literalinclude:: ../../gozerbot/aliases.py + :pyobject: aliasset + """ + + aliases.data[fromm] = to + +@aliaslocked +def aliasdel(fromm): + + """ delete an alias. + + :param fromm: alias to delete + :type fromm: string + + .. literalinclude:: ../../gozerbot/aliases.py + :pyobject: aliasdel + """ + + try: + + del aliases.data[fromm] + return 1 + + except KeyError: + pass + +def aliasget(fromm): + + """ retrieve an alias. + + :param fromm: alias to get command for + :type fromm: string + + .. literalinclude:: ../../gozerbot/aliases.py + :pyobject: aliasget + """ + + if aliases.data.has_key(fromm): + return aliases.data[fromm] + +### INIT SECTION + +# the aliases object +aliases = Persist(datadir + os.sep + 'aliases.new', init=False) +if not aliases.data: + aliases.data = {} + +### END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/admin.py +++ gozerbot-0.99.1/build/lib/gozerbot/admin.py @@ -0,0 +1,19 @@ +# gozerbot/admin.py +# +# + +""" gozerbot admin related funtions. """ + + +from simplejson import loads + +try: + cmndtable = loads(open('gozerdata' + os.sep + 'run' + os.sep + 'cmndtable').read()) +except: + cmndtable = {} + +try: + pluginlist = loads(open('gozerdata' + os.sep + 'run' + os.sep + 'pluginlist').read()) +except: + pluginlist = [] + --- gozerbot-0.99.1.orig/build/lib/gozerbot/datadir.py +++ gozerbot-0.99.1/build/lib/gozerbot/datadir.py @@ -0,0 +1,57 @@ +# gozerbot/datadir.py +# -*- coding: utf-8 -*- +# + +""" + :mod: `gozerbot.datadir` -- the datadir of the bot + + .. data:: + datadir .. points to the datadir of the bot + +""" + +__copyright__ = 'this file is in the public domain' + +# IMPORT SECTION + +# basic imports +import re, os + +# END IMPORT + +def makedirs(ddir=None): + + """ + make subdirs in datadir. users, db, fleet, pgp, plugs and old. + + .. literalinclude:: ../../gozerbot/datadir.py + :pyobject: makedirs + + """ + + ddir = ddir or datadir + curdir = os.getcwd() + + if not os.path.isdir(ddir): + os.mkdir(ddir) + if not os.path.isdir(ddir + '/run/'): + os.mkdir(ddir + '/run/') + if not os.path.isdir(ddir + '/users/'): + os.mkdir(ddir + '/users/') + if not os.path.isdir(ddir + '/db/'): + os.mkdir(ddir + '/db/') + if not os.path.isdir(ddir + '/fleet/'): + os.mkdir(ddir + '/fleet/') + if not os.path.isdir(ddir + '/pgp/'): + os.mkdir(ddir + '/pgp/') + if not os.path.isdir(ddir + '/plugs/'): + os.mkdir(ddir + '/plugs/') + if not os.path.isdir(ddir + '/old/'): + os.mkdir(ddir + '/old/') + +# INIT SECTION + +# the datadir +datadir = 'gozerdata' + +# END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/reboot.py +++ gozerbot-0.99.1/build/lib/gozerbot/reboot.py @@ -0,0 +1,74 @@ +# gozerbot/utils/reboot.py +# +# + +""" + reboot code. + +""" + +## IMPORT SECTION + +from gozerbot.fleet import fleet +from gozerbot.config import config + +from simplejson import dump + +import os, sys, pickle, tempfile + +## END IMPORT + +## LOCK SECTION + +# no locks + +## END LOCK + +def reboot(): + + """ + reboot the bot. + + .. literalinclude:: ../../gozerbot/reboot.py + :pyobject: reboot + + """ + + fleet.exit() + os.execl(sys.argv[0], *sys.argv) + +def reboot_stateful(bot, ievent, fleet, partyline): + """ + reboot the bot, but keep the connections. + + :param bot: bot on which the reboot command is given + :type bot: gozerbot.botbase.BotBase + :param ievent: event that triggered the reboot + :type ievent: gozerbot.eventbase. EventBase + :param fleet: the fleet of the bot + :type fleet: gozerbot.fleet.Fleet + :param partyline: partyline of the bot + :type partyline: gozerbot.partyline.PartyLine + + .. literalinclude:: ../../gozerbot/reboot.py + :pyobject: reboot_stateful + + """ + config.reload() + session = {'bots': {}, 'name': bot.name, 'channel': ievent.channel, 'partyline': []} + + for i in fleet.bots: + session['bots'].update(i._resumedata()) + + session['partyline'] = partyline._resumedata() + sessionfile = tempfile.mkstemp('-session', 'gozerbot-')[1] + dump(session, open(sessionfile, 'w')) + fleet.save() + fleet.exit(jabber=True) + os.execl(sys.argv[0], sys.argv[0], '-r', sessionfile) + +## INIT SECTION + +# no vars + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/fleet.py +++ gozerbot-0.99.1/build/lib/gozerbot/fleet.py @@ -0,0 +1,654 @@ +# gozerbot/fleet.py +# +# + +""" fleet is a list of bots. """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports + +from gozerbot.datadir import datadir +from utils.exception import handle_exception +from utils.generic import waitforqueue +from utils.log import rlog +from utils.locking import lockdec +from threads.thr import start_new_thread, threaded +from config import Config, fleetbotconfigtxt, config +from users import users +from plugins import plugins +from simplejson import load + +# basic imports + +import Queue, os, types, threading, time, pickle, glob, logging, shutil, thread + +# END IMPORT + +# ============ +# LOCK SECTION + +fleetlock = thread.allocate_lock() +fleetlocked = lockdec(fleetlock) + +# END LOCK +# ======== + +## START + +class FleetBotAlreadyExists(Exception): + pass + + +class Fleet(object): + + """ + a fleet contains multiple bots (list of bots). used the datadir + set in gozerbot/datadir.py + + """ + + def __init__(self): + self.datadir = datadir + os.sep + 'fleet' + if hasattr(os, 'mkdir'): + if not os.path.exists(self.datadir): + os.mkdir(self.datadir) + self.startok = threading.Event() + self.bots = [] + + def getfirstbot(self): + + """ + return the main bot of the fleet. + + :rtype: gozerbot.botbase.BotBase + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.getfirstbot + + """ + self.startok.wait() + return self.bots[0] + + def getfirstjabber(self): + + """ + return the first jabber bot of the fleet. + + :rtype: gozerbot.botbase.BotBase + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.getfirstjabber + + """ + + self.startok.wait() + + for bot in self.bots: + if bot.type == 'xmpp': + return bot + + def size(self): + + """ + return number of bots in fleet. + + :rtype: integer + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.size + + """ + + return len(self.bots) + + def resume(self, sessionfile): + + """ + resume bot from session file. + + :param sessionfile: filename of the session data file + :type sessionfile: string + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.resume + """ + + # read JSON session file + session = load(open(sessionfile)) + + # resume bots in session file + for name in session['bots'].keys(): + reto = None + if name == session['name']: + reto = session['channel'] + start_new_thread(self.resumebot, (name, session['bots'][name], reto)) + + # allow 5 seconds for bots to resurrect + time.sleep(5) + + # set start event + self.startok.set() + + def makebot(self, name, cfg=None): + + """ + create a bot .. use configuration if provided. + + :param name: the name of the bot + :type name: string + :param cfg: configuration file for the bot + :type cfg: gozerbot.config.Config + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.makebot + + """ + + if self.byname(name): + raise FleetBotAlreadyExists("there is already a %s bot in the fleet" % name) + + rlog(10, 'fleet', 'making bot') + bot = None + + # if not config create a default bot + if not cfg: + cfg = Config(self.datadir + os.sep + name, 'config', inittxt=fleetbotconfigtxt) + cfg.save() + + # create bot based on type + if cfg['type'] == 'irc': + from gozerbot.irc.bot import Bot + bot = Bot(name, cfg) + elif cfg['type'] == 'xmpp' or cfg['type'] == 'jabber': + from gozerbot.xmpp.bot import Bot + bot = Bot(name, cfg) + elif cfg['type'] == 'gozernet': + from gozerbot.gozernet.bot import GozerNetBot + bot = GozerNetBot(name, cfg) + else: + rlog(10, 'fleet', '%s .. unproper type: %s' % (cfg['name'], cfg['type'])) + + # set bot name and initialize bot + if bot: + cfg['name'] = bot.name = name + self.initbot(bot) + return bot + + # failed to created the bot + raise Exception("can't make %s bot" % name) + + def resumebot(self, botname, data={}, printto=None): + + """ + resume individual bot. + + :param botname: name of the bot to resume + :type botname: string + :param data: resume data + :type data: dict + :param printto: whom to reply to that resuming is done + :type printto: nick or JID + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.resumebot + + """ + + # see if we need to exit the old bot + oldbot = self.byname(botname) + if oldbot: + oldbot.exit() + + # recreate config file of the bot + cfg = Config(datadir + os.sep + 'fleet' + os.sep + botname, 'config') + + # make the bot and resume (IRC) or reconnect (Jabber) + bot = self.makebot(botname, cfg) + rlog(100, 'fleet', 'bot made: %s' % str(bot)) + + if bot: + if oldbot: + self.replace(oldbot, bot) + else: + self.bots.append(bot) + + if not bot.jabber: + bot._resume(data, printto) + else: + start_new_thread(bot.connectwithjoin, ()) + + def start(self, botlist=[], enable=False): + + """ + startup the bots. + + :param botlist: list of bots to start .. if not provided the bots in the gozerdata/fleet dir will be used + :type botlist: list + :param enable: whether the bot should be enabled + :type enable: boolean + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.start + + """ + + # scan the fleet datadir for bots + dirs = [] + got = [] + for bot in botlist: + dirs.append(self.datadir + os.sep + bot) + + if not dirs: + dirs = glob.glob(self.datadir + os.sep + "*") + + for fleetdir in dirs: + + if fleetdir.endswith('fleet'): + continue + + rlog(10, 'fleet', 'found bot: ' + fleetdir) + cfg = Config(fleetdir, 'config') + + if not cfg: + rlog(10, 'fleet', "can't read %s config file" % fleetdir) + continue + + name = fleetdir.split(os.sep)[-1] + + if not name: + rlog(10, 'fleet', "can't read botname from %s config file" % \ +fleetdir) + continue + + if not enable and not cfg['enable']: + rlog(10, 'fleet', '%s bot is disabled' % name) + continue + else: + rlog(10, 'fleet', '%s bot is enabled' % name) + + if not name in fleetdir: + rlog(10, 'fleet', 'bot name in config file doesnt match dir name') + continue + + try: + bot = self.makebot(name, cfg) + except FleetBotAlreadyExists: + rlog(10, 'fleet', 'there is already a fleetbot with the name %s' % name) + continue + + if bot: + self.addbot(bot) + start_new_thread(bot.connectwithjoin, ()) + got.append(bot) + + # set startok event + self.startok.set() + + return got + + def save(self): + + """ + save fleet data and call save on all the bots. + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.save + + """ + + for i in self.bots: + + try: + i.save() + except Exception, ex: + handle_exception() + + def avail(self): + + """ + show available fleet bots. + + :rtype: list + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.avail + + """ + + return os.listdir(self.datadir) + + def list(self): + + """ + return list of bot names. + + :rtype: list + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.list + + """ + + result = [] + + for i in self.bots: + result.append(i.name) + + return result + + def stopall(self): + + """ + call stop() on all fleet bots. + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.stopall + + """ + + for i in self.bots: + + try: + i.stop() + except: + pass + + def byname(self, name): + + """ + return bot by name. + + :param name: name of the bot + :type name: string + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.byname + + """ + + for i in self.bots: + if name == i.name: + return i + + def replace(self, name, bot): + + """ + replace bot with a new bot. + + :param name: name of the bot to replace + :type name: string + :param bot: bot to replace old bot with + :type bot: gozerbot.botbase.BotBase + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.replace + + """ + + for i in range(len(self.bots)): + if name == self.bots[i].name: + self.bots[i] = bot + return + + def initbot(self, bot): + + """ + initialise a bot. + + :param bot: bot to initialise + :type bot: gozerbot.botbase.BotBase + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.initbot + + """ + + if bot not in self.bots: + + if not os.path.exists(self.datadir + os.sep + bot.name): + os.mkdir(self.datadir + os.sep + bot.name) + + if type(bot.cfg['owner']) == types.StringType or type(bot.cfg['owner']) == types.UnicodeType: + bot.cfg['owner'] = [bot.cfg['owner'], ] + bot.cfg.save() + + users.make_owner(config['owner'] + bot.cfg['owner']) + rlog(10, 'fleet', 'added bot: ' + bot.name) + + @fleetlocked + def addbot(self, bot): + + """ + add a bot to the fleet .. remove all existing bots with the + same name. + + :param bot: bot to add + :type bot: gozerbot.botbase.BotBase + :rtype: boolean + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.addbot + + """ + + if bot: + + for i in range(len(self.bots)-1, -1, -1): + if self.bots[i].name == bot.name: + rlog(10, 'fleet', 'removing %s from fleet' % bot.name) + del self.bots[i] + + rlog(10, 'fleet', 'adding %s' % bot.name) + self.bots.append(bot) + return True + + return False + + def connect(self, name): + + """ + connect bot to the server. + + :param name: name of the bot + :type name: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.connect + + """ + + for i in self.bots: + + if i.name == name: + got = i.connect() + + if got: + start_new_thread(i.joinchannels, ()) + return True + else: + return False + + @fleetlocked + def delete(self, name): + + """ + delete bot with name from fleet. + + :param name: name of bot to delete + :type name: string + :rtype: boolean + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.delete + + """ + + for i in self.bots: + + if i.name == name: + i.exit() + self.remove(i) + i.cfg['enable'] = 0 + i.cfg.save() + rlog(10, 'fleet', '%s disabled' % i.name) + return True + + return False + + + def remove(self, bot): + + """ + delete bot by object. + + :param bot: bot to delete + :type bot: gozerbot.botbase.BotBase + :rtype: boolean + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.remove + + """ + + try: + self.bots.remove(bot) + return True + except ValueError: + return False + + def exit(self, name=None, jabber=False): + + """ + call exit on all bots. if jabber=True only jabberbots will exit. + + :param name: name of the bot to exit. if not provided all bots will exit. + :type name: string + :param jabber: flag to set when only jabberbots should exit + :type jabber: boolean + :rtype: boolean + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.exit + + """ + + if not name: + threads = [] + + for i in self.bots: + if jabber and not i.jabber: + pass + else: + threads.append(start_new_thread(i.exit, ())) + + for thr in threads: + thr.join() + + return + + + for i in self.bots: + + if i.name == name: + try: + i.exit() + except: + handle_exception() + self.remove(i) + return True + + return False + + def cmnd(self, event, name, cmnd): + + """ + do command on a bot. + + :param event: event to pass on to the dispatcher + :type event: gozerbot.event.EventBase + :param name: name of the bot to pass on to the dispatcher + :type name: string + :param cmnd: command to execute on the fleet bot + :type cmnd: string + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.cmnd + + """ + + bot = self.byname(name) + + if not bot: + return 0 + + from gozerbot.eventbase import EventBase + j = plugins.clonedevent(bot, event) + j.onlyqueues = True + j.txt = cmnd + q = Queue.Queue() + j.queues = [q] + j.speed = 3 + start_new_thread(plugins.trydispatch, (bot, j)) + result = waitforqueue(q) + + if not result: + return + + res = ["<%s>" % bot.name, ] + res += result + event.reply(res) + + def cmndall(self, event, cmnd): + + """ + do a command on all bots. + + :param event: event to pass on to dispatcher + :type event: gozerbot.eventbase.EventBase + :param cmnd: the command string to execute + :type cmnd: string + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.cmndall + + """ + + threads = [] + + for i in self.bots: + thread = start_new_thread(self.cmnd, (event, i.name, cmnd)) + threads.append(thread) + + for i in threads: + i.join() + + def broadcast(self, txt): + + """ + broadcast txt to all bots. + + :param txt: text to broadcast on all bots + :type txt: string + + .. literalinclude:: ../../gozerbot/fleet.py + :pyobject: Fleet.broadcast + + """ + + for i in self.bots: + i.broadcast(txt) + +# ============ +# INIT SECTION + + +# main fleet object +fleet = Fleet() + +# END INIT +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/eggs.py +++ gozerbot-0.99.1/build/lib/gozerbot/eggs.py @@ -0,0 +1,188 @@ +# gozerbot/eggs.py +# +# + +""" + + :mod: `gozerbot.eggs` -- eggs related functions + + this module is used to load the eggs on which gozerbot depends from + specified dir .. most of the time this is the gozernest dir. + +""" + +__copyright__ = 'this file is in the public domain' + +# IMPORT SECTION + +import os, sys +from utils.exception import handle_exception +from utils.log import rlog + +# END IMPORT + +mainenv = None + +def init(eggdir, log=False): + + """ + make sure setuptools is available. + + :param eggdir: directory to scan for eggs + :type eggdir: string + :param log: whether to log the registration of the setuptools egg + :type log: True or False + + .. literalinclude:: ../../gozerbot/eggs.py + :pyobject: init + + """ + + try: + import setuptools + except ImportError, ex: + try: + sys.path.insert(0, eggdir) + for egg in os.listdir(eggdir): + if egg.startswith('setuptools'): + log and rlog(10, 'eggs', 'loaded %s' % egg) + sys.path.insert(0, eggdir + os.sep + egg) + except OSError: + pass + +latest = {} + +def enable_egg(env, egg, log=True): + + """ + search for the latest version of an egg in the enviroment and put + it on sys.path. + + :param env: the environment to search the egg in + :type env: pkg_resources.Environment + :param egg: egg to load or find a newer version for + :param log: determine if we should log the enabling of the egg + + .. literalinclude:: ../../gozerbot/eggs.py + :pyobject: enable_egg + + """ + + try: + from pkg_resources import DistributionNotFound, VersionConflict, working_set, parse_requirements, require + + if not latest.has_key(egg.project_name): + latest[egg.project_name] = egg + + req = egg.as_requirement() + reqstr = str(req) + reqq = parse_requirements([reqstr.replace('==', '>='), ]) + for e in working_set.resolve(reqq, mainenv): + if e.location not in sys.path: + env.add(e) + working_set.add(e) + working_set.add_entry(e.location) + latest[egg.project_name] = e + sys.path.insert(0, egg.location) + log and rlog(3, 'eggs', 'loaded %s' % e) + else: + log and rlog(3, 'eggs', '%s already on path' % e) + except DistributionNotFound, ex: + env.add(egg) + working_set.add(egg) + working_set.add_entry(egg.location) + latest[egg.project_name] = egg + sys.path.insert(0, egg.location) + log and rlog(3, 'eggs', 'loaded %s' % egg) + except VersionConflict, ex: + if egg > ex[0]: + env.add(egg) + working_set.add_entry(egg.location) + working_set.add(egg) + latest[egg.project_name] = egg + sys.path.insert(0, egg.location) + log and rlog(3, 'eggs', 'override %s' % egg) + +def loadegg(name, eggdirs=['gozernest',], log=True): + + """ + scan eggdir for a egg matching `name`. + + :param name: piece of txt which should be in the egg projectname + :type name: string + :param eggdirs: directories to search in + :type eggdirs: list + :param log: boolean which indicates whether loading should be logged + :type log: boolean + + .. literalinclude:: ../../gozerbot/eggs.py + :pyobject: loadegg + + """ + + try: + from pkg_resources import find_distributions, Environment + global mainenv + + for eggdir in eggdirs: + if mainenv: + mainenv += Environment(eggdir) + else: + mainenv = Environment(eggdir) + eggs = find_distributions(eggdir) + for egg in eggs: + if name.lower() in egg.project_name.lower(): + enable_egg(mainenv, egg, log) + + except ImportError: + return + except Exception, ex: + handle_exception() + +def loadeggs(eggdir, log=True): + + """ + load all eggs in a directory. + + :param eggdir: directory to load eggs from + :type eggdir: string + + .. literalinclude:: ../../gozerbot/eggs.py + :pyobject: loadeggs + + """ + + rlog(3, 'eggs', 'scanning %s' % eggdir) + try: + from pkg_resources import find_distributions, Environment + global mainenv + + if mainenv: + mainenv += Environment(eggdir) + else: + mainenv = Environment(eggdir) + eggs = find_distributions(eggdir) + for egg in eggs: + if not egg.project_name.startswith('setuptools'): + enable_egg(mainenv, egg, log) + + except ImportError: + return + except Exception, ex: + handle_exception() + + res = [] + for name, egg in latest.iteritems(): + res.append("%s: %s" % (name, egg.version)) + + rlog(3, 'eggs', 'loaded: %s' % ' .. '.join(res)) + + +# INIT SECTION + + +# first search for setuptools and load it +init(os.getcwd()) +init(os.getcwd() + os.sep + 'gozernest') + +# END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/persistconfig.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/persistconfig.py @@ -0,0 +1,420 @@ +# gozerbot/persistconfig.py +# +# + +""" allow data to be pickled to disk .. creating the persisted object + restores data + +usage: + !plug-cfg -> shows list of all config + !plug-cfg key value -> sets value to key + !plug-cfg key -> shows list of key + !plug-cfg key add value -> adds value to list + !plug-cfg key remove value -> removes value from list + !plug-cfg key clear -> clears entire list + !plug-cfgsave -> force save configuration to disk + +todo: + - plugin api (more work needed?) + +""" + +__copyright__ = 'this file is in the public domain' +__author__ = 'Bas van Oostveen' + +from gozerbot.utils.log import rlog +from gozerbot.utils.trace import calledfrom +from gozerbot.compat.persist import Persist +from gozerbot.commands import cmnds, Command +from gozerbot.examples import examples +from gozerbot.datadir import datadir +import sys, os, types, time + +class Option(object): + + def __init__(self, value, desc, perm, example, name, exposed): + assert isinstance(name, str), "Option.self.name must be a string" + self.value = value + self.desc = desc + self.perm = perm + self.example = example + self.name = name.lower() + self.exposed = exposed + + def __casattr__(self, name, val): + # Update option if needed + if not hasattr(self, name): + setattr(self, name, val) + return True + else: + #if val and getattr(self, name)!=val: # old style + if val != None and type(getattr(self, name)) != type(val): + setattr(self, name, val) + return True + return False + + def check(self, key, plugname, value, desc, perm, example, name, exposed): + upd = False + # maybe checking value is too much here + if self.__casattr__("value", value): upd = True + if self.__casattr__("example", example): upd = True + if self.__casattr__("name", name): upd = True + if self.__casattr__("perm", perm): upd = True + if self.__casattr__("exposed", exposed): upd = True + if self.name == None: + self.name = "%s-cfg-%s" % (plugname, str(key)) + upd = True + return upd + + def __lt__(self, other): + return self.value < other + + def __le__(self, other): + return self.value <= other + + def __eq__(self, other): + return self.value == other + + def __ne__(self, other): + return self.value != other + + def __gt__(self, other): + return self.value >= other + + def __ge__(self, other): + return self.value >= other + +class LazyValueDict(object): + + """ emulates the normal Persist.data (key, value) dict """ + + def __init__(self, cfg): + self.__cfg = cfg + + def __len__(self): + return len(self.__persistData) + + def __getitem__(self, key): + return self.__cfg.data[key].value + + def __setitem__(self, key, value): + if not self.__cfg.data.has_key(key) or not \ +isinstance(self.__cfg.data[key], Option): + name = "%s-cfg-%s" % (self.__cfg.plugname, str(key)) + self.__cfg.define(value, "", 'OPER', "", name, exposed=False) + self.__cfg.set(key, value) + + def __delitem__(self, key): + raise Exception("Direct deletion not supported, use \ +persistConfig.undefine()") + + def __iter__(self): + return self.__cfg.data.__iter__() + + def iterkeys(self): + return self.__iter__() + + def __contains__(self, item): + return self.__cfg.data.has_key(item) + +class PersistConfigError(Exception): pass + +class PersistConfig(Persist): + + """ persist plugin configuration and create default handlers """ + + def __init__(self): + self.__basename__ = self.__class__.__name__ + self.plugname = calledfrom(sys._getframe()) + Persist.__init__(self, os.path.join(datadir, "%s-config" % \ +self.plugname), {}) + self.__callbacks = {} + cmndname = "%s-cfg" % self.plugname + rlog(-3, 'persistconfig', 'added command %s (%s)' % (cmndname, \ +self.plugname)) + cmnds[cmndname] = Command(self.cmnd_cfg, 'OPER', self.plugname, \ +threaded=True) + examples.add(cmndname, "plugin configuration", cmndname) + cmndnamesave = cmndname + "save" + cmnds[cmndnamesave] = Command(self.cmnd_cfgsave, 'OPER', \ +self.plugname, threaded=True) + examples.add(cmndnamesave, "save plugin configuration", cmndnamesave) + + def __getattribute__(self, name): + # make sure the attribute data is not called from Persist or + # PersistConfig returning a persist compatible (key, value) dict + # instead of the rich persistconfig + cf = calledfrom(sys._getframe()) + ## (key, option(value, ...)) is only for persistconfig internal usage. + if name == "data" and cf != "persistconfig" and cf != "persist" and \ +cf != self.__basename__: + # intercept data block, return a clean dict with lazy binding + # to option.value + return LazyValueDict(self) + return super(PersistConfig, self).__getattribute__(name) + + def handle_callback(self, event, key, value=None): + if self.__callbacks.has_key((key, event)): + cb, extra_data = self.__callbacks[(key, event)] + if callable(cb): + cb(key, value, event, extra_data) + else: + rlog(5, 'persistconfig', 'invalid callback for %s %s' % (key, \ +event)) + del self.__callbacks[(key, event)] + + ### cmnds + + def show_cfg(self, bot, ievent): + s = [] + for key, option in sorted(self.data.items()): + if not isinstance(option, Option): + rlog(5, 'persistconfig', 'Option %s is not a valid option' % \ +key) + continue + if not option.exposed: + continue + v = option.value + if type(v) in [str, unicode]: + v = '"'+v+'"' + v = str(v) + s.append("%s=%s" % (key, v)) + ievent.reply("options: " + ' .. '.join(s)) + + def cmnd_cfgsave(self, bot, ievent): + self.save() + ievent.reply("config saved") + + def cmnd_cfg_edit(self, bot, ievent, args, key, option): + if type(option.value) == types.ListType: + if args[0].startswith("[") and args[-1].endswith("]"): + values = [] + for v in ' '.join(args)[1:-1].replace(", ", ",").split(","): + if v[0]=='"' and v[-1]=='"': + # string + v = v.replace('"', '') + elif v[0]=="'" and v[-1]=="'": + # string + v = v.replace("'", "") + elif '.' in v: + # float + try: + v = float(v) + except ValueError: + ievent.reply("invalid long literal: %s" % v) + return + else: + # int + try: + v = int(v) + except ValueError: + ievent.reply("invalid int literal: %s" % v) + return + values.append(v) + self.set(key, values) + ievent.reply("%s set %s" % (key, values)) + return + command = args[0] + value = ' '.join(args[1:]) + if command == "clear": + self.clear(key) + ievent.reply("list empty") + elif command == "add": + self.append(key, value) + ievent.reply("%s added %s" % (key, value)) + elif command == "remove" or command == "del": + try: + self.remove(key, value) + ievent.reply("%s removed" % str(value)) + except ValueError: + ievent.reply("%s is not in list" % str(value)) + else: + ievent.reply("invalid command") + return + else: + value = ' '.join(args) + try: + value = type(option.value)(value) + except: + pass + if type(value) == type(option.value): + self.set(key, value) + ievent.reply("%s set" % key) + elif type(value) == types.LongType and \ +type(option.value) == types.IntType: + # allow upscaling from int to long + self.set(key, value) + ievent.reply("%s set" % key) + else: + ievent.reply("value %s (%s) is not of the same type as %s \ +(%s)" % (value, type(value), option.value, type(option.value))) + + def cmnd_cfg(self, bot, ievent): + if not ievent.args: + self.show_cfg(bot, ievent) + return + argc = len(ievent.args) + key = ievent.args[0] + try: + option = self.data[key] + except KeyError: + ievent.reply("%s option %s not found" % (self.plugname, key)) + return + if not isinstance(option, Option): + rlog(5, 'persistconfig', 'Option %s is not a valid option' % key) + return + if not option.exposed: + return + if argc == 1: + ievent.reply(str(option.value)) + return + self.cmnd_cfg_edit(bot, ievent, ievent.args[1:], key, option) + + def generic_cmnd(self, key): + def func(bot, ievent): + try: + option = self.data[key] + except KeyError: + ievent.reply("%s not found" % key) + # need return ? + if not isinstance(option, Option): + rlog(5, 'persistconfig', 'Option %s is not a valid option' % \ +key) + return + if ievent.args: + value = ' '.join(ievent.args) + try: + value = type(option.value)(value) + except: + pass + self.cmnd_cfg_edit(bot, ievent, ievent.args, key, option) + else: + ievent.reply(str(option.value)) + return func + + ### plugin api + + def define(self, key, value=None, desc="plugin option", perm='OPER', \ +example="", name=None, exposed=True): + if name: + name = name.lower() + if not self.data.has_key(key): + if name == None: + name = "%s-cfg-%s" % (self.plugname, str(key)) + option = Option(value, desc, perm, example, name, exposed) + self.data[key] = option + self.save() + else: + option = self.data[key] + # if unpickled option is not of class Option recreate the option + # also if the type of value has changed recreate the option + # exception if value got upgraded from int to long, then nothing + # has to be changed + if not isinstance(option, Option): + if name == None: + name = "%s-cfg-%s" % (self.plugname, str(key)) + if type(value) == type(option): + value = option + option = Option(value, desc, perm, example, name, exposed) + self.data[key] = option + self.save() + else: + if type(option.value) == types.LongType and \ +type(value) == types.IntType: + value = long(value) + if option.check(key, self.plugname, value, desc, perm, \ +example, name, exposed): + self.data[key] = option + self.save() + + def undefine(self, key, throw=False): + try: + option = self.data[key] + if option.exposed: + if cmnds.has_key(option.name): + del cmnds[option.name] + if examples.has_key(option.name): + del examples[option.name] + del self.data[key] + self.save() + return True + except KeyError, e: + if throw: + raise + return False + + def checkoption(self, key): + if not isinstance(self.data[key], Option): + raise PersistConfigError("Option %s is not a valid option" % key) + return True + + def set(self, key, value, throw=False): + if type(value)==unicode: + value = str(value) + try: + self.checkoption(key) + except (KeyError, PersistConfigError): + if throw: + raise + self.define(key, value) + self.handle_callback('change', key, value) + else: + self.data[key].value = value + self.handle_callback('change', key, value) + self.save() + + def append(self, key, value): + self.checkoption(key) + self.data[key].value.append(value) + self.save() + self.handle_callback('change', key, value) + self.handle_callback('add', key, value) + + def remove(self, key, value): + self.checkoption(key) + self.data[key].value.remove(value) + self.save() + self.handle_callback('change', key, value) + self.handle_callback('remove', key, value) + + def clear(self, key): + self.checkoption(key) + self.data[key].value = [] + self.save() + self.handle_callback('change', key, []) + self.handle_callback('clear', key) + + def get(self, key, default=None): + try: + return self.data[key].value + except KeyError: + return default + + def has_key(self, key): + return self.data.has_key(key) + + def callback(self, key, event, callback, extra_data=None): + callbacks = ["change", "add", "remove", "clear"] + if not event in callbacks: + raise PersistConfigError("Unsupported callback event %s" % event) + self.__callbacks[(key, event)] = (callback, extra_data) + + def syncold(self, filename, remove=False): + """ sync with old config data """ + if os.path.isfile(filename): + synckey = "persistconfig-syncold" + oldconfig = Persist(filename) + if not oldconfig.data.has_key(synckey): + rlog(10, 'persistconfig', "syncing old config %s with \ +persistconfig" % filename) + for i, j in oldconfig.data.iteritems(): + if i == synckey: + continue + if j and not self.get(i): + self.set(i, oldconfig.data[i]) + oldconfig.data[synckey] = time.localtime() + oldconfig.save() + del oldconfig + if remove: + os.unlink(filename) + --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/persist.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/persist.py @@ -0,0 +1,64 @@ +# gozerbot/persist.py +# +# + +""" allow data to be pickled to disk .. creating the persisted object + restores data +""" + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.log import rlog +import cPickle, thread, os, copy + +saving = [] +stopsave = 0 + +class Persist(object): + + """ persist data attribute to pickle file """ + + def __init__(self, filename, default=None): + rlog(1, 'compat-persist', 'reading %s' % filename) + self.fn = filename + self.lock = thread.allocate_lock() + self.data = None + # load data from pickled file + try: + datafile = open(filename, 'r') + except IOError: + if default != None: + self.data = copy.deepcopy(default) + return + try: + self.data = cPickle.load(datafile) + datafile.close() + except: + if default != None: + self.data = copy.deepcopy(default) + else: + rlog(100, 'compat-persist', 'ERROR: %s' % filename) + raise + + def save(self): + """ save persist data """ + if stopsave: + rlog(100, 'compat-persist', 'stopping mode .. not saving %s' % self.fn) + return + try: + saving.append(str(self.fn)) + self.lock.acquire() + # first save to temp file and when done rename + tmp = self.fn + '.tmp' + try: + datafile = open(tmp, 'w') + except IOError: + rlog(100, 'compat-persist', "can't save %s" % self.fn) + return + cPickle.dump(self.data, datafile) + datafile.close() + os.rename(tmp, self.fn) + rlog(10, 'compat-persist', '%s saved' % self.fn) + finally: + saving.remove(self.fn) + self.lock.release() --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/users.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/users.py @@ -0,0 +1,246 @@ +# gozerbot/users.py +# +# + +""" bot's users """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.datadir import datadir +from gozerbot.utils.log import rlog +from gozerbot.utils.generic import stripident, stripidents +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.generic import die, stripped +from gozerbot.compat.persist import Persist +from gozerbot.config import config +import re, types, os + +config.load() + +class User(object): + + """ repesents a user """ + + def __init__(self, name, userhosts, perms): + self.name = str(name) + self.userhosts = list(userhosts) + self.perms = list(perms) + self.email = "" + self.permit = [] + self.status = [] + self.passwd = "" + self.allowed = [] + self.notallowed = [] + self.tempuserhosts = [] + self.userdata = {} + + def __str__(self): + return "name: %s userhosts: %s perms: %s email: %s status: %s \ +allowed: %s notallowed: %s tempusershosts: %s permit: %s" % (self.name, \ +self.userhosts, self.perms, self.email, self.status, self.allowed, \ +self.notallowed, self.tempuserhosts, self.permit) + +class Users(Persist): + + """ holds multiple users """ + + def __init__(self, filename): + self.userhosts = {} + self.masks = {} + self.compiled = {} + Persist.__init__(self, filename) + if not self.data: + self.data = [] + for i in self.data: + for j in i.userhosts: + self.adduserhost(j, i) + + def adduserhost(self, userhost, user): + """ add userhost/mask """ + if '?' in userhost or '*' in userhost: + tmp = re.escape(userhost) + tmp = tmp.replace('\?','.') + tmp = tmp.replace('\*','.*?') + regex = re.compile(tmp) + self.compiled[regex] = user + self.masks[userhost] = regex + else: + self.userhosts[userhost] = user + + def deluserhost(self, userhost): + """ del userhost/mask """ + try: + if '?' in userhost or '*' in userhost: + regex = self.masks[userhost] + del self.compiled[regex] + del self.masks[userhost] + else: + del self.userhosts[userhost] + return 1 + except KeyError: + return 0 + + def exist(self, name): + """ see if user with username exists """ + name = name.lower() + for i in self.data: + if i.name == name: + return 1 + + def getperms(self, userhost): + """ get permissions """ + user = self.getuser(userhost) + if user: + return user.perms + else: + return ['ANON', ] + + def getuser(self, userhost): + """ get user for which userhost matches """ + userhost = stripident(userhost) + if userhost in self.userhosts: + return self.userhosts[userhost] + else: + for i in self.compiled: + if re.search(i, userhost): + return self.compiled[i] + for user in self.data: + for i in user.userhosts: + if i == userhost or i == stripped(userhost): + return user + return None + + def gotperm(self, name, perm): + user = self.byname(name) + if not user: + return 0 + if perm in user.perms: + return 1 + + def size(self): + """ return nr of users """ + return len(self.data) + + def add(self, name, userhosts, perms): + """ add user """ + self.addnosave(name, userhosts, perms) + self.save() + return 1 + + def addnosave(self, name, userhosts, perms): + """ add user without saving """ + name = name.lower() + for item in self.data: + if item.name == name: + return 0 + userhosts = stripidents(userhosts) + # add user + user = User(name, userhosts, perms) + self.data.append(user) + rlog(10, 'users', 'added user %s %s with perms %s' % (name, \ +userhosts, perms)) + for i in userhosts: + self.adduserhost(i, user) + return 1 + + def permitted(self, userhost, who, what): + """ check if (who,what) is in users permit list """ + user = self.getuser(userhost) + if not user: + return 0 + if (who, what) in user.permit: + return 1 + + def names(self): + """ get names of all users """ + result = [] + for item in self.data: + result.append(item.name) + return result + + def getname(self, userhost): + """ get name of user with userhost """ + item = self.getuser(userhost) + if item: + return item.name + else: + return None + + def byname(self, name): + """ return user with name """ + name = name.lower() + for item in self.data: + if item.name.lower() == name: + return item + return None + + def merge(self, name, userhost): + """ add userhosts to user with name """ + name = name.lower() + for item in self.data: + if item.name == name: + userhost = stripident(userhost) + item.userhosts.append(userhost) + self.adduserhost(userhost, item) + self.save() + rlog(10, 'users', 'merged %s (%s) with %s' % (name, \ +userhost, item.name)) + return 1 + return None + + def delete(self, name): + """ delete user with name """ + data = self.data + name = name.lower() + got = 0 + for itemnr in range(len(data)-1, -1, -1): + if data[itemnr].name == name: + for i in data[itemnr].userhosts: + self.deluserhost(i) + del data[itemnr] + got = 1 + if got: + self.save() + return 1 + return None + + def allowed(self, userhost, perms, log=True): + """ check if user with userhosts is allowed to execute perm command """ + if type(perms) != types.ListType: + perms = [perms, ] + if 'ANY' in perms: + return 1 + item = self.getuser(userhost) + if not item: + if log: + rlog(10, 'users', '%s userhost denied' % userhost) + return 0 + for i in perms: + if i in item.perms: + return 1 + if log: + rlog(10, 'users', '%s perm %s denied' % (userhost, perms)) + return 0 + + def status(self, userhost, status): + """ check if user has status set """ + status = status.upper() + item = self.getuser(userhost) + if not item: + return 0 + if status in item.status: + return 1 + return 0 + + def getemail(self, name): + """ return email of user """ + user = self.byname(name) + return user.email + + def setemail(self, name, email): + """ set email of user """ + user = self.byname(name) + user.email = email + self.save() + return 1 + --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/karma.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/karma.py @@ -0,0 +1,270 @@ +# plugs/karma.py +# +# + +""" karma plugin """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.redispatcher import rebefore +from gozerbot.datadir import datadir +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec +from gozerbot.utils.statdict import Statdict +from gozerbot.aliases import aliases +from gozerbot.plughelp import plughelp +from gozerbot.config import config +import thread, pickle, time, os + +plughelp.add('karma', 'maintain karma of items .. use ++ to raise karma by 1 \ +or use -- to lower by 1 .. reason might be given after a "#"') + +savelist = [] + +class Karma: + + """ holds karma data """ + + def __init__(self, ddir): + rlog(0, 'karma', 'reading %s' % datadir + os.sep + 'karma') + self.datadir = ddir + self.lock = thread.allocate_lock() + try: + karmafile = open(ddir + os.sep + 'karma', 'r') + self.karma = pickle.load(karmafile) + karmafile.close() + except: + self.karma = {} + try: + reasonupfile = open(ddir + os.sep + 'reasonup', 'r') + self.reasonup = pickle.load(reasonupfile) + reasonupfile.close() + except: + self.reasonup = {} + try: + reasondownfile = open(ddir + os.sep + 'reasondown', 'r') + self.reasondown = pickle.load(reasondownfile) + reasondownfile.close() + except: + self.reasondown = {} + try: + whoupfile = open(ddir + os.sep + 'whoup', 'r') + self.whoup = pickle.load(whoupfile) + whoupfile.close() + except: + self.whoup = {} + try: + whodownfile = open(ddir + os.sep + 'whodown', 'r') + self.whodown = pickle.load(whodownfile) + whodownfile.close() + except: + self.whodown = {} + + def size(self): + return len(self.karma) + + def save(self): + """ save karma data """ + try: + self.lock.acquire() + karmafile = open(self.datadir + os.sep + 'karma', 'w') + pickle.dump(self.karma, karmafile) + karmafile.close() + rlog(1, 'karma', '%s karma saved' % self.datadir) + reasonupfile = open(self.datadir + os.sep + 'reasonup', 'w') + pickle.dump(self.reasonup, reasonupfile) + reasonupfile.close() + rlog(1, 'karma', '%s reasonup saved' % self.datadir) + reasondownfile = open(self.datadir + os.sep + 'reasondown', 'w') + pickle.dump(self.reasondown, reasondownfile) + reasondownfile.close() + rlog(1, 'karma', '%s reasondown saved' % self.datadir) + whoupfile = open(self.datadir + os.sep + 'whoup', 'w') + pickle.dump(self.whoup, whoupfile) + whoupfile.close() + rlog(1, 'karma', '%s whoup saved' % self.datadir) + whodownfile = open(self.datadir + os.sep + 'whodown', 'w') + pickle.dump(self.whoup, whodownfile) + whodownfile.close() + rlog(1, 'karma', '%s whodown saved' % self.datadir) + finally: + self.lock.release() + + def add(self, item, value): + """ set karma value of item """ + self.karma[item.lower()] = value + + def delete(self, item): + """ delete karma item """ + item = item.lower() + try: + del self.karma[item] + return 1 + except KeyError: + return 0 + + def get(self, item): + """ get karma of item """ + item = item.lower() + if self.karma.has_key(item): + return self.karma[item] + else: + return None + + def addwhy(self, item, updown, reason): + """ add why of karma up/down """ + item = item.lower() + if not self.karma.has_key(item): + return 0 + reason = reason.strip() + if updown == 'up': + if self.reasonup.has_key(item): + self.reasonup[item].append(reason) + else: + self.reasonup[item] = [reason] + elif updown == 'down': + if self.reasondown.has_key(item): + self.reasondown[item].append(reason) + else: + self.reasondown[item] = [reason] + + def upitem(self, item, reason=None): + """ up a karma item with/without reason """ + item = item.lower() + if self.karma.has_key(item): + self.karma[item] += 1 + else: + self.karma[item] = 1 + if reason: + reason = reason.strip() + if self.reasonup.has_key(item): + self.reasonup[item].append(reason) + else: + self.reasonup[item] = [reason] + + def down(self, item, reason=None): + """ lower a karma item with/without reason """ + item = item.lower() + if self.karma.has_key(item): + self.karma[item] -= 1 + else: + self.karma[item] = -1 + if reason: + reason = reason.strip() + if self.reasondown.has_key(item): + self.reasondown[item].append(reason) + else: + self.reasondown[item] = [reason] + + def whykarmaup(self, item): + """ get why of karma ups """ + item = item.lower() + if self.reasonup.has_key(item): + return self.reasonup[item] + + def whykarmadown(self, item): + """ get why of karma downs """ + item = item.lower() + if self.reasondown.has_key(item): + return self.reasondown[item] + + def setwhoup(self, item, nick): + """ set who upped a karma item """ + item = item.lower() + if self.whoup.has_key(item): + self.whoup[item].append(nick) + else: + self.whoup[item] = [nick] + + def setwhodown(self, item, nick): + """ set who lowered a karma item """ + item = item.lower() + if self.whodown.has_key(item): + self.whodown[item].append(nick) + else: + self.whodown[item] = [nick] + + def getwhoup(self, item): + """ get list of who upped a karma item """ + item = item.lower() + try: + return self.whoup[item] + except KeyError: + return None + + def getwhodown(self, item): + """ get list of who downed a karma item """ + item = item.lower() + try: + return self.whodown[item] + except KeyError: + return None + + def good(self, limit=10): + """ show top 10 of karma items """ + statdict = Statdict() + for i in self.karma.keys(): + if i.startswith('quote '): + continue + statdict.upitem(i, value=self.karma[i]) + return statdict.top(limit=limit) + + def bad(self, limit=10): + """ show lowest 10 of negative karma items """ + statdict = Statdict() + for i in self.karma.keys(): + if i.startswith('quote '): + continue + statdict.upitem(i, value=self.karma[i]) + return statdict.down(limit=limit) + + def quotegood(self, limit=10): + """ show top 10 of karma items """ + statdict = Statdict() + for i in self.karma.keys(): + if not i.startswith('quote '): + continue + statdict.upitem(i, value=self.karma[i]) + return statdict.top(limit=limit) + + def quotebad(self, limit=10): + """ show lowest 10 of negative karma items """ + statdict = Statdict() + for i in self.karma.keys(): + if not i.startswith('quote '): + continue + statdict.upitem(i, value=self.karma[i]) + return statdict.down(limit=limit) + + def search(self, item): + """ search karma items """ + result = [] + item = item.lower() + for i, j in self.karma.iteritems(): + if item in i: + result.append((i, j)) + return result + + def whatup(self, nick): + """ show what items where upped by nick """ + nick = nick.lower() + statdict = Statdict() + for i, j in self.whoup.iteritems(): + for z in j: + if nick == z: + statdict.upitem(i) + return statdict.top() + + def whatdown(self, nick): + """ show what items where lowered by nick """ + nick = nick.lower() + statdict = Statdict() + for i, j in self.whodown.iteritems(): + for z in j: + if nick == z: + statdict.upitem(i) + return statdict.top() + --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/dbusers.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/dbusers.py @@ -0,0 +1,264 @@ +# gozerbot/dbusers.py +# +# + +""" bots users for mysql interface""" + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.log import rlog +from gozerbot.database.db import Db +import types + +class Dbusers(object): + + """ users class """ + + def __init__(self): + self.db = Db() + + def size(self): + """ return nr of users """ + result = self.db.execute(""" SELECT DISTINCT COUNT(*) FROM userhosts \ +""") + if result: + return result[0][0] + + def getperms(self, userhost): + """ return permission of user""" + name = self.getname(userhost) + if not name: + return ['ANON', ] + result = self.db.execute(""" SELECT perm FROM perms WHERE name = %s \ +""", name) + res = [] + for i in result: + res.append(i[0]) + return res + + def exist(self, name): + """ see if user with exists """ + name = name.lower() + result = self.db.execute(""" SELECT name,userhost FROM userhosts WHERE \ +name = %s """, name) + return result + + def getname(self, userhost): + """ get name of user belonging to """ + result = self.db.execute(""" SELECT name FROM userhosts WHERE \ +%s LIKE userhost """, userhost) + if result: + return result[0][0] + + def add(self, name, userhosts, perms): + """ add an user """ + if type(userhosts) != types.ListType: + rlog(10, 'dbusers', 'i need a list of userhosts') + return 0 + for i in userhosts: + self.adduserhost(name, i) + for i in perms: + self.addperm(name, i) + rlog(10, 'users', '%s added to user database' % name) + return 1 + + def adduserhost(self, name, userhost): + """ add userhost """ + name = name.lower() + res = None + result = self.db.execute(""" INSERT INTO userhosts(name, userhost) \ +values(%s, %s) """, (name, userhost)) + if result: + res = 1 + rlog(10, 'users', '%s (%s) added to userhosts' % (name, userhost)) + return res + + def addperm(self, name, perm): + """ add permission """ + name = name.lower() + perm = perm.upper() + res = None + result = self.db.execute(""" INSERT INTO perms(name, perm) \ +values(%s, %s) """, (name, perm)) + if result: + res = 1 + rlog(10, 'users', '%s perm %s added' % (name, perm)) + return res + + def delperm(self, name, perm): + """ add permission """ + name = name.lower() + perm = perm.upper() + result = self.db.execute(""" DELETE FROM perms WHERE name = %s AND \ +perm = %s """, (name, perm)) + if result: + rlog(10, 'users', '%s perm %s deleted' % (name, perm)) + return result + + def permitted(self, userhost, who, what): + """ check if (who,what) is in users permit list """ + name = self.getname(userhost) + res = None + if name: + result = self.db.execute(""" SELECT permit FROM permits WHERE \ +name = %s """, name) + if result: + for i in result: + if "%s %s" % (who, what) == i[0]: + res = 1 + return res + + def names(self): + """ get names of all users """ + res = [] + result = self.db.execute(""" SELECT DISTINCT name FROM userhosts """) + if result: + for i in result: + res.append(i[0]) + return res + + def merge(self, name, userhost): + """ add userhosts to user with name """ + name = name.lower() + if not self.exist(name): + return 0 + res = None + result = self.db.execute(""" INSERT INTO userhosts(userhost, name) \ +VALUES (%s, %s) """, (userhost, name)) + if result: + res = 1 + return res + + def delete(self, name): + """ delete user with name """ + name = name.lower() + res = None + nr1 = self.db.execute(""" DELETE FROM userhosts WHERE name = %s \ +""", name) + nr2 = self.db.execute(""" DELETE FROM perms WHERE name = %s \ +""", name) + if nr1 and nr2: + res = 1 + return res + + def status(self, userhost, status): + """ check if user with has set """ + name = self.getname(userhost) + res = None + if name: + status = status.upper() + result = self.db.execute(""" SELECT status FROM statuses WHERE \ +name = %s """, name) + if result: + for i in result: + if status == i[0]: + res = 1 + return res + + def gotperm(self, name, perm): + """ check if user had permission """ + name = name.lower() + perm = perm.upper() + result = self.db.execute(""" SELECT perm FROM perms WHERE \ +name = %s """, name) + if result: + for i in result: + if i[0] == perm: + return True + + def gotstatus(self, name, status): + """ check if user has status """ + name = name.lower() + status = status.upper() + result = self.db.execute(""" SELECT status FROM statuses WHERE \ +name = %s """, name) + if result: + for i in result: + if status == i[0]: + return True + + def gotuserhost(self, name, userhost): + """ check if user has userhost """ + name = name.lower() + result = self.db.execute(""" SELECT userhost FROM userhosts WHERE \ +name = %s """, name) + if result: + for i in result: + if i[0] == userhost: + return True + + def gotpermit(self, name, permit): + """ check if user permits something """ + name = name.lower() + result = self.db.execute(""" SELECT permit FROM permits WHERE \ +name = %s """, name) + if result: + for i in result: + if "%s %s" % permit == i[0]: + return True + + def allowed(self, userhost, perms, log=True): + """ check if user with userhosts is allowed to execute perm command """ + if not type(perms) == types.ListType: + perms = [perms, ] + if 'ANY' in perms: + return 1 + res = None + name = self.getname(userhost) + if not name: + if log: + rlog(10, 'users', '%s userhost denied' % userhost) + return res + result = self.db.execute(""" SELECT perm FROM perms WHERE \ +name = %s """, name) + if result: + for i in result: + if i[0] in perms: + res = 1 + if not res: + if log: + rlog(10, 'users', "%s perm %s denied" % (userhost, perms)) + return res + + def getemail(self, name): + """ get email of user """ + name = name.lower() + email = None + email = self.db.execute(""" SELECT email FROM email WHERE name = %s \ +""", name) + if email: + return email[0][0] + + def setemail(self, name, email): + """ set email of user """ + res = 0 + try: + result = self.db.execute(""" INSERT INTO email(name, email) \ +VALUES (%s, %s) """, (name, email)) + except: + try: + result = self.db.execute(""" UPDATE email SET email = %s \ +WHERE name = %s """, (email, name)) + except: + pass + if result: + res = 1 + return res + + def addpermall(self, perm): + """ add permission to all users """ + perm = perm.upper() + for i in self.names(): + try: + self.addperm(i, perm) + except: + pass + + def delpermall(self, perm): + """ delete permission from all users """ + perm = perm.upper() + for i in self.names(): + try: + self.delperm(i, perm) + except: + pass --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/pdod.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/pdod.py @@ -0,0 +1,75 @@ +# gozerbot/pdod.py +# +# + +""" pickled dicts of dicts """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.locking import lockdec +from persist import Persist +import thread + +pdodlock = thread.allocate_lock() +locked = lockdec(pdodlock) + +class Pdod(Persist): + + """ pickled dicts of dicts """ + + def __init__(self, filename): + Persist.__init__(self, filename) + if not self.data: + self.data = {} + + def __getitem__(self, name): + """ return item with name """ + if self.data.has_key(name): + return self.data[name] + + @locked + def save(self): + Persist.save(self) + + @locked + def __delitem__(self, name): + """ delete name item """ + if self.data.has_key(name): + return self.data.__delitem__(name) + + @locked + def __setitem__(self, name, item): + """ set name item """ + self.data[name] = item + + def __contains__(self, name): + return self.data.__contains__(name) + + @locked + def setdefault(self, name, default): + """ set default of name """ + return self.data.setdefault(name, default) + + def has_key(self, name): + """ has name key """ + return self.data.has_key(name) + + def has_key2(self, name1, name2): + """ has [name1][name2] key """ + if self.data.has_key(name1): + return self.data[name1].has_key(name2) + + def get(self, name1, name2): + """ get data[name1][name2] """ + try: + result = self.data[name1][name2] + return result + except KeyError: + return None + + @locked + def set(self, name1, name2, item): + """ set name, name2 item """ + if not self.data.has_key(name1): + self.data[name1] = {} + self.data[name1][name2] = item --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/__init__.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/__init__.py @@ -0,0 +1,32 @@ +# gozerbot package +# +# + +""" gozerbot compatibility package. """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +from gozerbot.eggs import loadegg +import os + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +# no locks + +# END LOCK +# ======== + +# ============ +# INIT SECTION + +loadegg('feedparser', [os.getcwd(), os.getcwd() + os.sep + 'gozernest'], log=False) + +# END INIT +# ======== \ No newline at end of file --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/config.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/config.py @@ -0,0 +1,313 @@ +# gozerbot/config.py +# +# + +""" this is where the config dict lives .. use pickle to persist config data + .. use this pickle on start until the config file has changed """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.datadir import datadir +import os, pickle, subprocess + +# version string +ver = 'GOZERBOT 0.8.1.1 RELEASE' + +def diffdict(dictfrom, dictto): + """ check for differences between two dicts """ + temp = {} + for i in dictto.iteritems(): + if dictfrom.has_key(i[0]): + if dictfrom[i[0]] != i[1]: + temp.setdefault(i[0], i[1]) + else: + temp.setdefault(i[0], i[1]) + return temp + +class Config(dict): + + """ config object is a dict """ + + def __init__(self, ddir, *args, **kw): + dict.__init__(self, *args, **kw) + self.dir = str(ddir) + self['dbtype'] = 'mysql' + + def __getitem__(self, item): + """ get config item .. return None if not available""" + if not self.has_key(item): + return None + else: + return dict.__getitem__(self, item) + + def set(self, item, value): + """ set a config item """ + dict.__setitem__(self, item, value) + self.save() + + def load(self): + """ load the config file """ + frompickle = {} + picklemodtime = None + # first do reload of the data/config file + self.reload() + # see if there is a configpickle + try: + picklemodtime = os.stat(self.dir + os.sep + 'configpickle')[8] + configpickle = open(self.dir + os.sep + 'configpickle', 'r') + frompickle = pickle.load(configpickle) + configpickle.close() + except OSError: + return + except: + pass + # see if data/config is more recent than the configpickle + configmodtime = os.stat(self.dir + os.sep + 'config')[8] + if picklemodtime and picklemodtime > configmodtime: + # if so update the config dict with the pickled data + # a = diffdict(self, frompickle) + self.update(frompickle) + # set version + if self['dbenable']: + self['version'] = ver + ' ' + self['dbtype'].upper() + else: + self['version'] = ver + + def save(self): + """ save config data to pickled file """ + picklefile = open(self.dir + os.sep + 'configpickle', 'w') + pickle.dump(self, picklefile) + picklefile.close() + + def reload(self): + """ use execfile to reload data/config """ + try: + execfile(self.dir + os.sep + 'config', self) + except IOError: + self.defaultconfig() + # remove builtin data + try: + del self['__builtins__'] + except: + pass + # set version + if self['dbenable']: + self['version'] = ver + ' ' + self['dbtype'].upper() + else: + self['version'] = ver + + def defaultconfig(self): + """ init default config values if no config file is found """ + self['loglevel'] = 100 + self['jabberenable'] = 0 + self['ircdisable'] = 0 + self['stripident'] = 1 + self['owneruserhost'] = ['bart@127.0.0.1', ] + self['nick'] = 'gozerbot' + self['server'] = 'localhost' + self['port'] = 6667 + self['ipv6'] = 0 + self['username'] = 'gozerbot' + self['realname'] = 'GOZERBOT' + self['defaultcc'] = "!" + self['nolimiter'] = 0 + self['quitmsg'] = 'http://gozerbot.org' + self['dbenable'] = 0 + self['udp'] = 0 + self['partyudp'] = 0 + self['mainbotname'] = 'main' + self['addonallow'] = 0 + self['allowedchars'] = [] + +configtxt = """# config +# +# + +__copyright__ = 'this file is in the public domain' + +# gozerdata dir umask +umask = 0700 + +# logging level .. the higher this value is the LESS the bot logs +loglevel = 10 + +## jabber section: + +jabberenable = 0 +jabberowner = 'bartholo@localhost' +jabberhost = 'localhost' +jabberuser = 'gozerbot@localhost' +jabberpass = 'pass' +jabberoutsleep = 0.1 + +## irc section: + +ircdisable = 0 + +# stripident .. enable stripping of ident from userhost +stripident = 1 + +# userhost of owner .. make sure this matches your client's userhost +# if it doesn't match you will get an userhost denied message when you +# try to send commands to the bot +owneruserhost = ['bart@127.0.0.1', ] + +# the nick the bot tries to use, only used if no nick is set +# otherwise the bot will use the last nick used by the !nick command +nick = 'gozerbot' + +# alternick +#alternick = 'gozerbot2' + +# server to connect to +server = 'localhost' + +# irc port to connect to +port = 6667 + +# ircd password for main bot +#password = 'bla' + +# ipv6 +ipv6 = 0 + +# bindhost .. uncomment and edit to use +#bindhost = 'localhost' + +# bots username +username = 'gozerbot' + +# realname +realname = 'GOZERBOT' + +# default control character +defaultcc = "!" + +# no limiter +nolimiter = 0 + +# quit message +quitmsg = 'http://gozerbot.org' + +# nickserv .. set pass to enable nickserv ident +nickservpass = "" +nickservtxt = ['set unfiltered on', ] + +## if you want to use a database: + +dbenable = 0 # set to 1 to enable +dbtype = 'mysql' # one of mysql or sqlite +dbname = "gb_db" +dbhost = "localhost" +dbuser = "bart" +dbpasswd = "mekker2" +dboldstyle = False # set to True if mysql database is <= 4.1 + +## if you want to use udp: + +# udp +udp = 0 # set to 1 to enable +partyudp = 0 +udpipv6 = 0 +udphost = 'localhost' +udpport = 5500 +udpmasks = ['192.168*', ] +udpallow = ['127.0.0.1', ] +udpallowednicks = ['#dunkbots', 'dunker'] +udppassword = 'mekker' +udpseed = "" # set this to 16 char wide string if you want to encrypt the data +udpstrip = 1 # strip all chars < char(32) +udpsleep = 0 # sleep in sendloop .. can be used to delay packet traffic + +# tcp +tcp = 0 # set to 1 to enable +partytcp = 0 +tcpipv6 = 0 +tcphost = 'localhost' +tcpport = 5500 +tcpmasks = ['192.168*', ] +tcpallow = ['127.0.0.1', ] +tcpallowednicks = ['#dunkbots', 'dunker', 'dunker@jabber.xs4all.nl'] +tcppassword = 'mekker' +tcpseed = "bla1234567890bla" +# set this to 16 char wide string if you want to encrypt the data +tcpstrip = 1 +tcpsleep = 0 + +## other stuff: + +# plugin server +pluginserver = 'http://gozerbot.org' + +# upgradeurl .. only needed if mercurial repo changed +#upgradeurl = 'http://gozerbot.org/hg/gozerbot' + +# mail related +mailserver = None +mailfrom = None + +# collective boot server +collboot = "gozerbot.org:8088" + +# name of the main bot +mainbotname = 'main' + +# allowed character for strippedtxt +allowedchars = [] + +# set to 1 to allow addons +addonallow = 0 + +# enable loadlist +loadlist = 0 +""" + +def writeconfig(): + """ wtite default config file to datadir/config """ + if not os.path.isfile(datadir + os.sep + 'config'): + cfgfile = open(datadir + os.sep + 'config', 'w') + cfgfile.write(configtxt) + cfgfile.close() + +loadlist = """ +core +misc +irc +not +grep +reverse +count +chanperm +choice +fleet +ignore +upgrade +job +reload +rest +tail +user +googletalk +all +at +backup +install +reload +tell +reverse +to +underauth +userstate +alias +nickserv +""" + +def writeloadlist(): + """ write loadlist to datadir """ + if not os.path.isfile(datadir + os.sep + 'loadlist'): + cfgfile = open(datadir + os.sep + 'loadlist', 'w') + cfgfile.write(loadlist) + cfgfile.close() + +# create the config dict and load it from file +#config = Config(datadir) --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/todo.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/todo.py @@ -0,0 +1,172 @@ +# plugs/todo.py +# +# + +from gozerbot.compat.persist import Persist +import time + +class Todoitem: + + """ a todo item """ + + def __init__(self, name, descr, ttime=None, duration=None, warnsec=None, \ +priority=None, num=0): + self.name = name + self.time = ttime + self.duration = duration + self.warnsec = warnsec + self.descr = descr + self.priority = priority + self.num = num + + def __str__(self): + return "name: %s num: %d time: %s duration: %s warnsec: %s \ +description: %s priority: %s" % (self.name, self.num, \ +time.ctime(self.time), self.duration, self.warnsec, self.descr, self.priority) + +class Todolist: + + """ a dict faking list of todo items .. index is number """ + + def __init__(self): + self.max = 0 + self.data = {} + + def __len__(self): + return len(self.data) + + def __getitem__(self, num): + return self.data[num] + + def __delitem__(self, num): + del self.data[num] + + def __iter__(self): + tmplist = self.data.values() + tmplist.sort(lambda x, y: cmp(x.priority, y.priority), reverse=True) + return tmplist.__iter__() + + def append(self, item): + """ add todo item """ + self.max += 1 + item.num = self.max + self.data[self.max] = item + + def __str__(self): + return str(self.data) + + +class Todo(Persist): + + """ Todoos """ + + def __init__(self, filename): + Persist.__init__(self, filename) + if not self.data: + return + for key in self.data.keys(): + todoos = self.data[key] + for (k, v) in todoos.data.items(): + v.num = k + newd = Todolist() + for i in todoos: + newd.append(i) + self.data[key] = newd + + def size(self): + """ return number of todo entries """ + return len(self.data) + + def get(self, name): + """ get todoos of """ + if self.data.has_key(name): + return self.data[name] + + def add(self, name, txt, ttime, warnsec=0): + """ add a todo """ + name = name.lower() + if not self.data.has_key(name): + self.data[name] = Todolist() + self.data[name].append(Todoitem(name, txt.strip(), ttime, \ +warnsec=0-warnsec)) + self.save() + return len(self.data[name]) + + def addnosave(self, name, txt, ttime): + """ add but don't save """ + name = name.lower() + if not self.data.has_key(name): + self.data[name] = Todolist() + self.data[name].append(Todoitem(name, txt, ttime)) + + def reset(self, name): + name = name.lower() + if self.data.has_key(name): + self.data[name] = Todolist() + self.save() + + def delete(self, name, nr): + """ delete todo item """ + if not self.data.has_key(name): + return 0 + todoos = self.data[name] + try: + if todoos[nr].warnsec: + alarmnr = 0 - todoos[nr].warnsec + if alarmnr > 0: + alarms.delete(alarmnr) + del todoos[nr] + except KeyError: + return 0 + self.save() + return 1 + + def toolate(self, name): + """ show if there are any time related todoos that are too late """ + now = time.time() + teller = 0 + for i in self.data[name]: + if i.time < now: + teller += 1 + return teller + + def timetodo(self, name): + """ show todoos with time field set """ + result = [] + if not self.data.has_key(name): + return result + for i in self.data[name]: + if i.time: + result.append(i) + return result + + def withintime(self, name, time1, time2): + """ show todoos within time frame """ + result = [] + if not self.data.has_key(name): + return result + for i in self.data[name]: + if i.time >= time1 and i.time < time2: + result.append(i) + return result + + def setprio(self, who, itemnr, prio): + """ set priority of todo item """ + try: + todoitems = self.get(who) + todoitems[itemnr].priority = prio + self.save() + return 1 + except (KeyError, TypeError): + pass + + def settime(self, who, itemnr, ttime): + """ set time of todo item """ + try: + todoitems = self.get(who) + todoitems[itemnr].time = ttime + self.save() + return 1 + except (KeyError, TypeError): + pass + --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/quote.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/quote.py @@ -0,0 +1,114 @@ +# plugs/quote.py +# +# + +from gozerbot.compat.persist import Persist + +class Quoteitem(object): + + """ object representing a quote """ + + def __init__(self, idnr, txt, nick=None, userhost=None, ttime=None): + self.id = idnr + self.txt = txt + self.nick = nick + self.userhost = userhost + self.time = ttime + +class Quotes(Persist): + + """ list of quotes """ + + def __init__(self, fname): + Persist.__init__(self, fname) + if not self.data: + self.data = [] + + def size(self): + """ return nr of quotes """ + return len(self.data) + + def add(self, nick, userhost, quote): + """ add a quote """ + id = nextid.next('quotes') + item = Quoteitem(id, quote, nick, userhost, \ +time.time()) + self.data.append(item) + self.save() + return id + + def addnosave(self, nick, userhost, quote, ttime): + """ add quote but don't call save """ + id = nextid.next('quotes') + item = Quoteitem(nextid.next('quotes'), quote, nick, userhost, ttime) + self.data.append(item) + return id + + def delete(self, quotenr): + """ delete quote with id == nr """ + for i in range(len(self.data)): + if self.data[i].id == quotenr: + del self.data[i] + self.save() + return 1 + + def random(self): + """ get random quote """ + if not self.data: + return None + quotenr = random.randint(0, len(self.data)-1) + return self.data[quotenr] + + def idquote(self, quotenr): + """ get quote by id """ + for i in self.data: + if i.id == quotenr: + return i + + def whoquote(self, quotenr): + """ get who quoted the quote """ + for i in self.data: + if i.id == quotenr: + return (i.nick, i.time) + + def last(self, nr=1): + """ get last quote """ + return self.data[len(self.data)-nr:] + + def search(self, what): + """ search quotes """ + if not self.data: + return [] + result = [] + andre = re.compile('and', re.I) + ands = re.split(andre, what) + got = 0 + for i in self.data: + for item in ands: + if i.txt.find(item.strip()) == -1: + got = 0 + break + got = 1 + if got: + result.append(i) + return result + + def searchlast(self, what, nr=1): + """ search quotes backwards limit to 1""" + if not self.data: + return [] + result = [] + andre = re.compile('and', re.I) + ands = re.split(andre, what) + got = 0 + for i in self.data[::-1]: + for item in ands: + if i.txt.find(item.strip()) == -1: + got = 0 + break + got = 1 + if got: + result.append(i) + got = 0 + return result + --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/rss.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/rss.py @@ -0,0 +1,1124 @@ +# plugs/rss.py +# +# + +"""the rss mantra is of the following: + +as OPER: + +. add a url with rss-add +. start the watcher with rss-watch + +now the user can start the bot sending messages of the feed to him with +rss-start. if an OPER gives a rss-start command in a channel data will be +send to the channel. + """ + +__copyright__ = 'this file is in the public domain' +__gendocfirst__ = ['rss-add', 'rss-watch', 'rss-start'] +__gendocskip__ = ['rss-dump', ] + +from gozerbot.compat.persist import Persist +from gozerbot.utils.url import geturl2 +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec +from gozerbot.utils.generic import strippedtxt, fromenc +from gozerbot.utils.url import striphtml, useragent +from gozerbot.utils.rsslist import rsslist +from gozerbot.utils.statdict import Statdict +from gozerbot.fleet import fleet +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.datadir import datadir +from gozerbot.utils.dol import Dol +from gozerbot.compat.pdod import Pdod +from gozerbot.compat.pdol import Pdol +from gozerbot.plughelp import plughelp +from gozerbot.periodical import periodical +from gozerbot.aliases import aliasset +from gozerbot.users import users +import feedparser +import gozerbot.threads.thr as thr +import time, os, types, thread, socket, xml + +plughelp.add('rss', 'manage rss feeds') + +savelist = [] + +def txtindicts(result, d): + """ return lowlevel values in (nested) dicts """ + for j in d.values(): + if type(j) == types.DictType: + txtindicts(result, j) + else: + result.append(j) + +def checkfordate(data, date): + for item in data: + try: + d = item['updated'] + except KeyError: + continue + if date == d: + return True + return False + +rsslock = thread.allocate_lock() +locked = lockdec(rsslock) + +class RssException(Exception): + pass + +class Rss301(RssException): + pass + +class RssStatus(RssException): + pass + +class RssBozoException(RssException): + pass + +class RssNoSuchItem(RssException): + pass + +class Rssitem(object): + + """ item that contains rss data """ + + def __init__(self, name, url, itemslist, watchchannels=[], \ +sleeptime=30*60): + self.name = name + self.url = url + self.itemslist = list(itemslist) + self.watchchannels = list(watchchannels) + self.sleeptime = int(sleeptime) + self.running = 0 + self.stoprunning = 0 + self.botname = None + + def __str__(self): + return "name=%s url=%s itemslist=%s watchchannels=%s sleeptime=%s \ +running=%s" % (self.name, self.url, str(self.itemslist), \ +str(self.watchchannels), str(self.sleeptime), self.running) + +class Rssdict(Persist): + + """ dict of rss entries """ + + def __init__(self, filename): + Persist.__init__(self, filename) + if not self.data: + self.data = {} + if self.data.has_key('itemslists'): + del self.data['itemslists'] + self.itemslists = Pdol(filename + '.itemslists') + self.handlers = {} + self.results = {} + self.jobids = {} + self.rawresults = {} + self.results = Dol() + self.modified = {} + self.etag = {} + self.markup = Pdod(filename + '.markup') + + def size(self): + """ return number of rss entries """ + return len(self.data) + + @locked + def add(self, name, url): + """ add rss item """ + rlog(10, 'rss', 'adding %s %s' % (name, url)) + self.data[name] = Rssitem(name, url, ['title', ]) + self.save() + + @locked + def delete(self, name): + """ delete rss item by name """ + target = None + for j, i in self.data.iteritems(): + if i.name == name: + target = i + if target: + try: + target.running = 0 + del self.data[name] + self.save() + except: + pass + + def byname(self, name): + """ return rss item by name """ + try: + return self.data[name] + except: + return + + def getdata(self, name): + """ get data of rss feed """ + rssitem = self.byname(name) + if rssitem == None: + raise RssNoSuchItem("no %s rss item found" % name) + try: + modified = self.modified[name] + except KeyError: + modified = None + try: + etag = self.etag[name] + except KeyError: + etag = None + result = feedparser.parse(rssitem.url, modified=modified, etag=etag, \ +agent=useragent()) + if result and result.has_key('bozo_exception'): + rlog(1, 'rss', '%s bozo_exception: %s' % (name, \ +result['bozo_exception'])) + #raise RssStatus(result['bozo_exception']) + try: + status = result.status + except AttributeError: + status = 200 + if status != 200 and status != 301 and status != 302 and status != 304: + raise RssStatus(status) + try: + self.modified[name] = result.modified + except AttributeError: + pass + try: + self.etag[name] = result.etag + except AttributeError: + pass + if status == 304: + return self.rawresults[name] + else: + self.rawresults[name] = result.entries + return result.entries + +class Rsswatcher(Rssdict): + + """ rss watchers """ + + def __init__(self, filename): + Rssdict.__init__(self, filename) + + def sync(self, name): + result = self.getdata(name) + if result: + self.results[name] = result + + def changeinterval(self, name, interval): + periodical.changeinterval(self.jobids[name], interval) + + @locked + def startwatchers(self): + """ start watcher threads """ + for j, i in self.data.iteritems(): + if i.running: + thr.start_new_thread(self.watch, (i.name, )) + + @locked + def stopwatchers(self): + """ stop all watcher threads """ + for j, i in self.data.iteritems(): + if i.running: + i.stoprunning = 1 + periodical.killgroup('rss') + + def dowatch(self, name, sleeptime=1800): + rssitem = self.byname(name) + if not rssitem: + rlog(10, 'rss', "no %s rss item available" % name) + return + while 1: + try: + self.watch(name) + except Exception, ex: + rlog(100, 'rss', '%s feed error: %s' % (name, str(ex))) + rlog(100, 'rss', '%s sleeping %s seconds for retry' % \ +(name, sleeptime)) + time.sleep(sleeptime) + if not rssitem.running: + break + else: + break + + def makeresult(self, name, target, data): + res = [] + for j in data: + tmp = {} + if not self.itemslists[(name, target)]: + return [] + for i in self.itemslists[(name, target)]: + try: + tmp[i] = unicode(j[i]) + except KeyError: + continue + res.append(tmp) + return res + + def watch(self, name): + """ start a watcher thread """ + # get basic data + rlog(10, 'rss', 'trying %s rss feed watcher' % name) + try: + result = self.getdata(name) + except RssException, ex: + rlog(10, 'rss', "%s error: %s" % (name, str(ex))) + result = [] + rssitem = self.byname(name) + if not rssitem: + raise RssNoItem() + # poll every sleeptime seconds + self.results[name] = result + pid = periodical.addjob(rssitem.sleeptime, 0, self.peek, name, name) + self.jobids[name] = pid + rlog(10, 'rss', 'started %s rss watch' % name) + + def makeresponse(self, name, res, channel, sep="\002||\002"): + # loop over result to make a response + result = "" + itemslist = self.itemslists[(name, channel)] + if not itemslist: + rssitem = self.byname(name) + if not rssitem: + return "no %s rss item" % name + else: + self.itemslists.extend((name, channel), rssitem.itemslist) + self.itemslists.save() + for j in res: + resultstr = "" + for i in self.itemslists[(name, channel)]: + try: + item = unicode(j[i]) + if not item: + continue + if item.startswith('http://'): + resultstr += "<%s> - " % item + else: + resultstr += "%s - " % striphtml(item) + except KeyError: + continue + resultstr = resultstr[:-3] + if resultstr: + result += "%s %s " % (resultstr, sep) + return result[:-6] + + def peek(self, name, *args): + rssitem = self.byname(name) + if not rssitem or not rssitem.running or rssitem.stoprunning: + return + try: + try: + res = self.getdata(name) + except socket.timeout: + rlog(10, 'rss', 'socket timeout of %s' % name) + return + except RssException, ex: + rlog(10, 'rss', '%s error: %s' % (name, str(ex))) + return + if not res: + return + res2 = [] + for j in res: + try: + d = j['date'] + except KeyError: + if j not in self.results[name]: + self.results[name].append(j) + res2.append(j) + else: + if not checkfordate(self.results[name], d): + self.results[name].append(j) + res2.append(j) + if not res2: + return + for item in rssitem.watchchannels: + try: + (botname, channel) = item + except: + rlog(10, 'rss', '%s is not in the format \ +(botname,channel)' % str(item)) + bot = fleet.byname(botname) + if not bot: + continue + if self.markup.get((name, channel), 'all-lines'): + for i in res2: + response = self.makeresponse(name, [i, ], channel) + bot.say(channel, "\002%s\002: %s" % \ +(rssitem.name, response), fromm=rssitem.name) + else: + sep = self.markup.get((name, channel), 'seperator') + if sep: + response = self.makeresponse(name, res2, channel, \ +sep=sep) + else: + response = self.makeresponse(name, res2, channel) + bot.say(channel, "\002%s\002: %s" % (rssitem.name, \ +response), fromm=rssitem.name) + except Exception, ex: + handle_exception(txt=name) + + @locked + def stopwatch(self, name): + """ stop watcher thread """ + for i, j in self.data.iteritems(): + if i == name: + j.running = 0 + try: + del self.results[name] + except KeyError: + pass + self.save() + try: + periodical.killjob(self.jobids[i]) + except KeyError: + pass + return 1 + + @locked + def list(self): + """ return of rss names """ + feeds = self.data.keys() + return feeds + + @locked + def runners(self): + """ show names/channels of running watchers """ + result = [] + for j, i in self.data.iteritems(): + if i.running == 1 and not i.stoprunning: + result.append((i.name, i.watchchannels)) + return result + + @locked + def feeds(self, botname, channel): + """ show names/channels of running watcher """ + result = [] + for j, i in self.data.iteritems(): + if (botname, channel) in i.watchchannels: + result.append(i.name) + return result + + @locked + def url(self, name): + """ return url of rssitem """ + for j, i in self.data.iteritems(): + if i.name == name: + return i.url + + def scan(self, name): + """ scan a rss url for used xml items """ + try: + result = self.getdata(name) + except RssException, ex: + rlog(10, 'rss', '%s error: %s' % (name, str(ex))) + return + if not result: + return + keys = [] + for item in self.rawresults[name]: + for key in item.keys(): + keys.append(key) + statdict = Statdict() + for key in keys: + statdict.upitem(key) + return statdict.top() + + def search(self, name, item, search): + res = [] + for result in self.rawresults[name]: + try: + title = result['title'] + txt = result[item] + except KeyError: + continue + if search in title.lower(): + if txt: + res.append(txt) + return res + + def searchall(self, item, search): + res = [] + for name, results in self.rawresults.iteritems(): + for result in results: + try: + title = result['title'] + txt = result[item] + except KeyError: + continue + if search in title.lower(): + if txt: + res.append("%s: %s" % (name, txt)) + return res + + def all(self, name, item): + res = [] + for result in self.rawresults[name]: + try: + txt = result[item] + except KeyError: + continue + if txt: + res.append(txt) + return res + +watcher = Rsswatcher(datadir + os.sep + 'old' + os.sep + 'rss') + +def init(): + """ called after plugin import """ + thr.start_new_thread(watcher.startwatchers, ()) + return 1 + +def size(): + """ return number of watched rss entries """ + return watcher.size() + +def shutdown(): + """ called before plugin import """ + watcher.stopwatchers() + return 1 + +def handle_rssadd(bot, ievent): + """ rss-add .. add a rss item """ + try: + (name, url) = ievent.args + except ValueError: + ievent.missing(' ') + return + watcher.add(name, url) + ievent.reply('rss item added') + +cmnds.add('rss-add', handle_rssadd, 'OPER') +examples.add('rss-add', 'rss-add to the rsswatcher', 'rss-add \ +gozerbot http://gozerbot.org/hg/gozerbot/?cmd=changelog;style=rss') + +def handle_rssdel(bot, ievent): + """ rss-del .. delete a rss item """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + if watcher.byname(name): + watcher.stopwatch(name) + watcher.delete(name) + ievent.reply('rss item deleted') + else: + ievent.reply('there is no %s rss item' % name) + +cmnds.add('rss-del', handle_rssdel, 'OPER') +examples.add('rss-del', 'rss-del .. remove from the \ +rsswatcher', 'rss-del mekker') + +def handle_rsswatch(bot, ievent): + """ rss-watch .. start watcher thread """ + if not ievent.channel: + ievent.reply('no channel provided') + try: + name, sleepsec = ievent.args + except ValueError: + try: + name = ievent.args[0] + sleepsec = 1800 + except IndexError: + ievent.missing(' [secondstosleep]') + return + try: + sleepsec = int(sleepsec) + except ValueError: + ievent.reply("time to sleep needs to be in seconds") + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss object" % name) + return + got = None + if not rssitem.running: + rssitem.sleeptime = sleepsec + rssitem.running = 1 + rssitem.stoprunning = 0 + got = True + watcher.save() + try: + watcher.watch(name) + except Exception, ex: + ievent.reply(str(ex)) + return + if got: + watcher.save() + ievent.reply('watcher started') + else: + ievent.reply('already watching %s' % name) + +cmnds.add('rss-watch', handle_rsswatch, 'OPER') +examples.add('rss-watch', 'rss-watch [seconds to sleep] .. go \ +watching ', '1) rss-watch gozerbot 2) rss-watch gozerbot 600') + +def handle_rssstart(bot, ievent): + """ rss-start .. start a rss feed to a user """ + if not ievent.rest: + ievent.missing('') + return + name = ievent.rest + rssitem = watcher.byname(name) + if bot.jabber: + target = ievent.userhost + else: + if users.allowed(ievent.userhost, ['OPER', ]) and not ievent.msg: + target = ievent.channel + else: + target = ievent.nick + if rssitem == None: + ievent.reply("we don't have a %s rss object" % name) + return + if not rssitem.running: + ievent.reply('%s watcher is not running' % name) + return + if (bot.name, target) in rssitem.watchchannels: + ievent.reply('we are already monitoring %s on (%s,%s)' % \ +(name, bot.name, target)) + return + rssitem.watchchannels.append((bot.name, target)) + for item in rssitem.itemslist: + watcher.itemslists.adduniq((name, target), item) + watcher.save() + ievent.reply('%s started' % name) + +cmnds.add('rss-start', handle_rssstart, ['RSS', 'USER']) +examples.add('rss-start', 'rss-start .. start a rss feed \ +(per user/channel) ', 'rss-start gozerbot') + +def handle_rssstop(bot, ievent): + """ rss-start .. start a rss feed to a user """ + if not ievent.rest: + ievent.missing('') + return + name = ievent.rest + rssitem = watcher.byname(name) + if bot.jabber: + target = ievent.userhost + else: + if users.allowed(ievent.userhost, ['OPER', ]) and not ievent.msg: + target = ievent.channel + else: + target = ievent.nick + if rssitem == None: + ievent.reply("we don't have a %s rss feed" % name) + return + if not rssitem.running: + ievent.reply('%s watcher is not running' % name) + return + if not (bot.name, target) in rssitem.watchchannels: + ievent.reply('we are not monitoring %s on (%s,%s)' % \ +(name, bot.name, target)) + return + rssitem.watchchannels.remove((bot.name, target)) + watcher.save() + ievent.reply('%s stopped' % name) + +cmnds.add('rss-stop', handle_rssstop, ['RSS', 'USER']) +examples.add('rss-stop', 'rss-stop .. stop a rss feed \ +(per user/channel) ', 'rss-stop gozerbot') + +def handle_rsschannels(bot, ievent): + """ rss-channels .. show channels of rss feed """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing("") + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss object" % name) + return + if not rssitem.watchchannels: + ievent.reply('%s is not in watch mode' % name) + return + result = [] + for i in rssitem.watchchannels: + result.append(str(i)) + ievent.reply("channels of %s: " % name, result, dot=True) + +cmnds.add('rss-channels', handle_rsschannels, 'OPER') +examples.add('rss-channels', 'rss-channels .. show channels', \ +'rss-channels gozerbot') + +def handle_rssaddchannel(bot, ievent): + """ rss-addchannel [] .. add a channel to \ + rss item """ + try: + (name, botname, channel) = ievent.args + except ValueError: + try: + (name, channel) = ievent.args + botname = bot.name + except ValueError: + try: + name = ievent.args[0] + botname = bot.name + channel = ievent.channel + except IndexError: + ievent.missing(' [] ') + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss object" % name) + return + if not rssitem.running: + ievent.reply('%s watcher is not running' % name) + return + if (botname, channel) in rssitem.watchchannels: + ievent.reply('we are already monitoring %s on (%s,%s)' % \ +(name, botname, channel)) + return + rssitem.watchchannels.append((botname, channel)) + watcher.save() + ievent.reply('%s added to %s rss item' % (channel, name)) + +cmnds.add('rss-addchannel', handle_rssaddchannel, 'OPER') +examples.add('rss-addchannel', 'rss-addchannel [] \ +..add or to watchchannels of ', \ +'1) rss-addchannel gozerbot #dunkbots 2) rss-addchannel gozerbot main \ +#dunkbots') + +def handle_rssadditem(bot, ievent): + try: + (name, item) = ievent.args + except ValueError: + ievent.missing(' ') + return + if bot.jabber or users.allowed(ievent.userhost, ['OPER', ]): + target = ievent.channel.lower() + else: + target = ievent.nick.lower() + if not watcher.byname(name): + ievent.reply("we don't have a %s feed" % name) + return + watcher.itemslists.adduniq((name, target), item) + watcher.itemslists.save() + #watcher.sync(name) + ievent.reply('%s added to (%s,%s) itemslist' % (item, name, target)) + +cmnds.add('rss-additem', handle_rssadditem, ['RSS', 'USER']) +examples.add('rss-additem', 'add a token to the itemslist (per user/channel)',\ + 'rss-additem gozerbot link') + +def handle_rssdelitem(bot, ievent): + try: + (name, item) = ievent.args + except ValueError: + ievent.missing(' ') + return + if users.allowed(ievent.userhost, ['OPER', 'RSS']): + target = ievent.channel.lower() + else: + target = ievent.nick.lower() + if not watcher.byname(name): + ievent.reply("we don't have a %s feed" % name) + return + try: + watcher.itemslists.remove((name, target), item) + watcher.itemslists.save() + except RssNoSuchItem: + ievent.reply("we don't have a %s rss feed" % name) + return + ievent.reply('%s removed from (%s,%s) itemslist' % (item, name, target)) + +cmnds.add('rss-delitem', handle_rssdelitem, ['RSS', 'USER']) +examples.add('rss-delitem', 'remove a token from the itemslist \ +(per user/channel)', 'rss-delitem gozerbot link') + +def handle_rssmarkup(bot, ievent): + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + if users.allowed(ievent.userhost, ['OPER', ]): + target = ievent.channel.lower() + else: + target = ievent.nick.lower() + try: + ievent.reply(str(watcher.markup[(name, target)])) + except KeyError: + pass + +cmnds.add('rss-markup', handle_rssmarkup, ['RSS', 'USER']) +examples.add('rss-markup', 'show markup list for a feed (per user/channel)', \ +'rss-markup gozerbot') + +def handle_rssaddmarkup(bot, ievent): + try: + (name, item, value) = ievent.args + except ValueError: + ievent.missing(' ') + return + if users.allowed(ievent.userhost, ['OPER', ]): + target = ievent.channel.lower() + else: + target = ievent.nick.lower() + try: + watcher.markup.set((name, target), item, value) + watcher.markup.save() + ievent.reply('%s added to (%s,%s) markuplist' % (item, name, target)) + except KeyError: + ievent.reply("no (%s,%s) feed available" % (name, target)) + +cmnds.add('rss-addmarkup', handle_rssaddmarkup, ['RSS', 'USER']) +examples.add('rss-addmarkup', 'add a markup option to the markuplist \ +(per user/channel)', 'rss-addmarkup gozerbot noseperator 1') + +def handle_rssdelmarkup(bot, ievent): + try: + (name, item) = ievent.args + except ValueError: + ievent.missing(' ') + return + if users.allowed(ievent.userhost, ['OPER', 'RSS']): + target = ievent.channel.lower() + else: + target = ievent.nick.lower() + try: + del watcher.markup[(name, target)][item] + except (KeyError, TypeError): + ievent.reply("can't remove %s from %s feed's markup" % (item, name)) + return + watcher.markup.save() + ievent.reply('%s removed from (%s,%s) markuplist' % (item, name, target)) + +cmnds.add('rss-delmarkup', handle_rssdelmarkup, ['RSS', 'USER']) +examples.add('rss-delmarkup', 'remove a markup option from the markuplist \ +(per user/channel)', 'rss-delmarkup gozerbot noseperator') + +def handle_rssdelchannel(bot, ievent): + """ rss-delchannel [] .. delete channel \ + from rss item """ + botname = None + try: + (name, botname, channel) = ievent.args + except ValueError: + try: + (name, channel) = ievent.args + botname = bot.name + except ValueError: + try: + name = ievent.args[0] + botname = bot.name + channel = ievent.channel + except IndexError: + ievent.missing(' [] []') + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss object" % name) + return + if (botname, channel) not in rssitem.watchchannels: + ievent.reply('we are not monitoring %s on (%s,%s)' % \ +(name, botname, channel)) + return + rssitem.watchchannels.remove((botname, channel)) + ievent.reply('%s removed from %s rss item' % (channel, name)) + watcher.save() + +cmnds.add('rss-delchannel', handle_rssdelchannel, 'OPER') +examples.add('rss-delchannel', 'rss-delchannel [] \ +[] .. delete or from watchchannels of \ +', '1) rss-delchannel gozerbot #dunkbots 2) rss-delchannel gozerbot \ +main #dunkbots') + +def handle_rssstopwatch(bot, ievent): + """ rss-stopwatch .. stop a watcher thread """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + rssitem = watcher.byname(name) + if not rssitem: + ievent.reply("there is no %s rssitem" % name) + return + if not watcher.stopwatch(name): + ievent.reply("can't stop %s watcher" % name) + return + ievent.reply('stopped %s rss watch' % name) + +cmnds.add('rss-stopwatch', handle_rssstopwatch, 'OPER') +examples.add('rss-stopwatch', 'rss-stopwatch .. stop polling ', \ +'rss-stopwatch gozerbot') + +def handle_rsssleeptime(bot, ievent): + """ rss-sleeptime .. get sleeptime of rss item """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss item" % name) + return + try: + ievent.reply('sleeptime for %s is %s seconds' % (name, \ +str(rssitem.sleeptime))) + except AttributeError: + ievent.reply("can't get sleeptime for %s" % name) + +cmnds.add('rss-sleeptime', handle_rsssleeptime, 'OPER') +examples.add('rss-sleeptime', 'rss-sleeptime .. get sleeping time \ +for ', 'rss-sleeptime gozerbot') + +def handle_rsssetsleeptime(bot, ievent): + """ rss-setsleeptime .. set sleeptime of rss item """ + try: + (name, sec) = ievent.args + sec = int(sec) + except ValueError: + ievent.missing(' ') + return + if sec < 60: + ievent.reply('min is 60 seconds') + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss item" % name) + return + rssitem.sleeptime = sec + if rssitem.running: + watcher.changeinterval(name, sec) + watcher.save() + ievent.reply('sleeptime set') + +cmnds.add('rss-setsleeptime', handle_rsssetsleeptime, 'OPER') +examples.add('rss-setsleeptime', 'rss-setsleeptime .. set \ +sleeping time for .. min 60 sec', 'rss-setsleeptime gozerbot 600') + +def handle_rssget(bot, ievent): + """ rss-get .. fetch rss data """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + rssitem = watcher.byname(name) + if rssitem == None: + ievent.reply("we don't have a %s rss item" % name) + return + try: + result = watcher.getdata(name) + except Exception, ex: + ievent.reply('%s error: %s' % (name, str(ex))) + return + response = watcher.makeresponse(name, result, ievent.channel) + if response: + ievent.reply("results of %s: %s" % (name, response)) + else: + ievent.reply("can't match watcher data") + +cmnds.add('rss-get', handle_rssget, ['RSS', 'USER', 'ANON']) +examples.add('rss-get', 'rss-get .. get data from ', \ +'rss-get gozerbot') + +def handle_rssrunning(bot, ievent): + """ rss-running .. show which watchers are running """ + result = watcher.runners() + resultlist = [] + teller = 1 + for i in result: + resultlist.append("%s %s" % (i[0], i[1])) + if resultlist: + ievent.reply("running rss watchers: ", resultlist, nr=1) + else: + ievent.reply('nothing running yet') + +cmnds.add('rss-running', handle_rssrunning, ['RSS', 'USER']) +examples.add('rss-running', 'rss-running .. get running rsswatchers', \ +'rss-running') + +def handle_rsslist(bot, ievent): + """ rss-list .. return list of rss items """ + result = watcher.list() + result.sort() + if result: + ievent.reply("rss items: ", result, dot=True) + else: + ievent.reply('no rss items yet') + +cmnds.add('rss-list', handle_rsslist, ['RSS', 'USER', 'ANON']) +examples.add('rss-list', 'get list of rss items', 'rss-list') + +def handle_rssurl(bot, ievent): + """ rss-url .. return url of rss item """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + result = watcher.url(name) + ievent.reply('url of %s: %s' % (name, result)) + +cmnds.add('rss-url', handle_rssurl, ['RSS', 'USER', 'ANON']) +examples.add('rss-url', 'rss-url .. get url from rssitem with \ +', 'rss-url gozerbot') + +def handle_rssitemslist(bot, ievent): + """ rss-itemslist .. show itemslist of rss item """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + try: + itemslist = watcher.itemslists[(name, ievent.channel.lower())] + except KeyError: + ievent.reply("no itemslist set for (%s, %s)" % (name, \ +ievent.channel.lower())) + return + ievent.reply("itemslist of (%s, %s): " % (name, ievent.channel.lower()), \ +itemslist, dot=True) + +cmnds.add('rss-itemslist', handle_rssitemslist, ['RSS', 'USER']) +examples.add('rss-itemslist', 'rss-itemslist .. get itemslist of \ + ', 'rss-itemslist gozerbot') + +def handle_rssscan(bot, ievent): + """ rss-scan .. scan rss item for used xml items """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + if not watcher.byname(name): + ievent.reply('no %s feeds available' % name) + return + try: + result = watcher.scan(name) + except Exception, ex: + ievent.reply(str(ex)) + return + if result == None: + ievent.reply("can't get data for %s" % name) + return + res = [] + for i in result: + res.append("%s=%s" % i) + ievent.reply("tokens of %s: " % name, res) + +cmnds.add('rss-scan', handle_rssscan, 'OPER') +examples.add('rss-scan', 'rss-scan .. get possible items of ', \ +'rss-scan gozerbot') + +def handle_rsssync(bot, ievent): + """ rss-sync .. sync rss item data """ + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + try: + result = watcher.sync(name) + ievent.reply('%s synced' % name) + except Exception, ex: + ievent.reply("%s error: %s" % (name, str(ex))) + +cmnds.add('rss-sync', handle_rsssync, 'OPER') +examples.add('rss-sync', 'rss-sync .. sync data of ', \ +'rss-sync gozerbot') + +def handle_rssfeeds(bot, ievent): + """ rss-feeds .. show what feeds are running in a channel """ + try: + channel = ievent.args[0] + except IndexError: + channel = ievent.printto + try: + result = watcher.feeds(bot.name, channel) + if result: + ievent.reply("feeds running in %s: " % channel, result, dot=True) + else: + ievent.reply('%s has no feeds running' % channel) + except Exception, ex: + ievent.reply("ERROR: %s" % str(ex)) + +cmnds.add('rss-feeds', handle_rssfeeds, ['USER', 'RSS']) +examples.add('rss-feeds', 'rss-feeds .. show what feeds are running \ +in a channel', '1) rss-feeds 2) rss-feeds #dunkbots') + +def handle_rsslink(bot, ievent): + try: + feed, rest = ievent.rest.split(' ', 1) + except ValueError: + ievent.missing(' ') + return + rest = rest.strip().lower() + try: + res = watcher.search(feed, 'link', rest) + if not res: + res = watcher.search(feed, 'feedburner:origLink', rest) + if res: + ievent.reply(res, dot=" \002||\002 ") + except KeyError: + ievent.reply('no %s feed data available' % feed) + return + +cmnds.add('rss-link', handle_rsslink, ['RSS', 'USER']) +examples.add('rss-link', 'give link of item which title matches search key', \ +'rss-link gozerbot gozer') + +def handle_rssdescription(bot, ievent): + try: + feed, rest = ievent.rest.split(' ', 1) + except ValueError: + ievent.missing(' ') + return + rest = rest.strip().lower() + res = "" + try: + ievent.reply(watcher.search(feed, 'description', rest), \ +dot=" \002||\002 ") + except KeyError: + ievent.reply('no %s feed data available' % feed) + return + +cmnds.add('rss-description', handle_rssdescription, ['RSS', 'USER']) +examples.add('rss-description', 'give description of item which title \ +matches search key', 'rss-description gozerbot gozer') + +def handle_rssall(bot, ievent): + try: + feed = ievent.args[0] + except IndexError: + ievent.missing('') + return + try: + ievent.reply(watcher.all(feed, 'title'), dot=" \002||\002 ") + except KeyError: + ievent.reply('no %s feed data available' % feed) + return + +cmnds.add('rss-all', handle_rssall, ['RSS', 'USER']) +examples.add('rss-all', "give titles of a feed", 'rss-all gozerbot') + +def handle_rsssearch(bot, ievent): + try: + txt = ievent.args[0] + except IndexError: + ievent.missing('') + return + try: + ievent.reply(watcher.searchall('title', txt), dot=" \002||\002 ") + except KeyError: + ievent.reply('no %s feed data available' % feed) + return + +cmnds.add('rss-search', handle_rsssearch, ['RSS', 'USER']) +examples.add('rss-search', "search titles of all current feeds", \ +'rss-search goz') + +def handle_rssdump(bot, ievent): + try: + ievent.reply(str(watcher.rawresults[ievent.rest])) + except Exception, ex: + ievent.reply(str(ex)) + +cmnds.add('rss-dump', handle_rssdump, 'OPER') +examples.add('rss-dump', 'dump cached rss data', 'rss-dump') --- gozerbot-0.99.1.orig/build/lib/gozerbot/compat/pdol.py +++ gozerbot-0.99.1/build/lib/gozerbot/compat/pdol.py @@ -0,0 +1,72 @@ +# gozerbot/pdol.py +# +# + +""" pickled dict of lists """ + +__copyright__ = 'this file is in the public domain' + +from persist import Persist + +class Pdol(Persist): + + """ pickled dict of lists """ + + def __init__(self, fname): + Persist.__init__(self, fname) + if not self.data: + self.data = {} + + def __iter__(self, name): + return self.data[name].__iter__() + + def __getitem__(self, item): + if self.data.has_key(item): + return self.data[item] + + def __delitem__(self, item): + if self.data.has_key(item): + self.data.__delitem__(item) + return 1 + + def __setitem__(self, item, what): + if self.data.has_key(item): + self.data[item].append(what) + else: + self.data[item] = [what] + return 1 + + def add(self, item, what): + """ add what to items list """ + return self.__setitem__(item, what) + + def adduniq(self, item, what): + """ add what to items list if item not yet added """ + if not self.data.has_key(item): + self.new(item) + if what not in self.data[item]: + return self.__setitem__(item, what) + + def get(self, item): + """ get items list """ + return self.__getitem__(item) + + def new(self, what): + """ reset list of what """ + self.data[what] = [] + + def delete(self, item, what): + """ remove what from item's list """ + del self.data[item][what] + + def extend(self, item, what): + if not self.data.has_key(item): + self.new(item) + self.data[item].extend(what) + + def remove(self, item, what): + try: + self.data[item].remove(what) + return 1 + except (ValueError, KeyError): + return 0 --- gozerbot-0.99.1.orig/build/lib/gozerbot/threads/thr.py +++ gozerbot-0.99.1/build/lib/gozerbot/threads/thr.py @@ -0,0 +1,163 @@ +# gozerbot/thr.py +# +# + +""" own threading wrapper. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.stats import stats +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec +from gozerbot.config import config + +# basic imports +import threading, re, time, thread + +dontshowthreads = ['Periodical.runjob', ] + +# regular expression to determine thread name +methodre = re.compile('method\s+(\S+)', re.I) +funcre = re.compile('function\s+(\S+)', re.I) + +threadlock = thread.allocate_lock() +locked = lockdec(threadlock) + +class Botcommand(threading.Thread): + + """ thread for running bot commands. """ + + def __init__(self, group, target, name, args, kwargs): + threading.Thread.__init__(self, None, target, name, args, kwargs) + self.name = name + self.ievent = args[1] + self.setDaemon(True) + + def join(self): + threading.Thread.join(self) + + def run(self): + + """ run the bot command. """ + + try: + rlog(10, 'thr', 'running bot command thread %s' % self.name) + stats.up('threads', self.name) + result = threading.Thread.run(self) + + if self.ievent.closequeue: + rlog(4, 'thr', 'closing queue for %s' % self.ievent.userhost) + for i in self.ievent.queues: + i.put_nowait(None) + + except Exception, ex: + handle_exception(self.ievent) + time.sleep(0.1) + +class Thr(threading.Thread): + + """ thread wrapper. """ + + def __init__(self, group, target, name, args, kwargs): + threading.Thread.__init__(self, None, target, name, args, kwargs) + rlog(-14, 'thr', 'running %s .. args: %s' % (name, args)) + self.setDaemon(True) + self.name = name + time.sleep(0.001) + + def join(self): + threading.Thread.join(self) + + def run(self): + """ run the thread. """ + try: + if self.name not in dontshowthreads: + rlog(-4, 'thr', 'running thread %s' % self.name) + stats.up('threads', self.name) + threading.Thread.run(self) + except Exception, ex: + handle_exception() + time.sleep(1) + +def getname(func): + + """ get name of function/method. """ + + name = "" + + method = re.search(methodre, str(func)) + if method: + name = method.group(1) + else: + function = re.search(funcre, str(func)) + if function: + name = function.group(1) + else: + name = str(func) + return name + +#@locked +def start_new_thread(func, arglist, kwargs=None): + + """ start a new thread .. set name to function/method name.""" + + if not kwargs: + kwargs = {} + + try: + name = getname(func) + + if not name: + name = 'noname' + + thread = Thr(None, target=func, name=name, args=arglist, \ +kwargs=kwargs) + + rlog(-1, 'thr', 'starting %s thread' % str(func)) + + thread.start() + return thread + + except: + handle_exception() + time.sleep(1) + +#@locked +def start_bot_command(func, arglist, kwargs={}): + + """ start a new thread .. set name to function/method name. """ + + if not kwargs: + kwargs = {} + + try: + name = getname(func) + + if not name: + name = 'noname' + + thread = Botcommand(group=None, target=func, name=name, args=arglist, \ +kwargs=kwargs) + + rlog(-1, 'thr', 'starting %s thread' % str(func)) + + thread.start() + return thread + + except: + handle_exception() + time.sleep(1) + +def threaded(func): + + """ threading decorator. """ + + def threadedfunc(*args, **kwargs): + if config['PAE']: + func(*args, **kwargs) + else: + start_new_thread(func, args, kwargs) + + return threadedfunc --- gozerbot-0.99.1.orig/build/lib/gozerbot/threads/threadloop.py +++ gozerbot-0.99.1/build/lib/gozerbot/threads/threadloop.py @@ -0,0 +1,135 @@ +# gozerbot/threads/threadloop.py +# +# + +""" class to implement start/stoppable threads. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.config import config +from gozerbot.utils.exception import handle_exception + +# core thread import +from thr import start_new_thread + +# basic imports +import Queue, time + +class ThreadLoop(object): + + """ implement startable/stoppable threads. """ + + def __init__(self, name="", queue=None): + self.name = name or 'idle' + self.stopped = False + self.running = False + self.outs = [] + self.queue = queue or Queue.Queue() + self.nowrunning = "none" + + def _loop(self): + rlog(0, "threadloop", 'starting threadloop') + while not self.stopped: + try: + self.running = True + try: + data = self.queue.get_nowait() + except Queue.Empty: + if self.stopped: + break + time.sleep(0.1) + continue + + if self.stopped: + break + + if not data: + break + rlog(0, self.name, 'running %s' % str(data)) + self.handle(*data) + except Exception, ex: handle_exception() + + self.running = False + rlog(0, self.name, 'stopping threadloop') + + def put(self, *data): + + """ put data on task queue. """ + + self.queue.put_nowait(data) + + def start(self): + + """ start the thread. """ + + if not self.running: + start_new_thread(self._loop, ()) + + def stop(self): + + """ stop the thread. """ + + self.stopped = True + self.running = False + self.queue.put(None) + + def handle(self, *args, **kwargs): + + """ overload this. """ + + pass + +class RunnerLoop(ThreadLoop): + + """ dedicated threadloop for bot commands/callbacks. """ + + + def _loop(self): + rlog(0, "runnerloop", 'starting threadloop') + self.running = True + + while not self.stopped: + + try: + data = self.queue.get() + except Queue.Empty: + if self.stopped: + break + time.sleep(0.1) + continue + + if self.stopped: + break + + if not data: + break + + self.nowrunning = data[0] + rlog(0, 'runnerloop', 'now running %s' % self.nowrunning) + self.handle(*data) + + self.running = False + rlog(0, 'runnerloop', 'stopping threadloop') + +class ThreadSleeper(ThreadLoop): + + def __init__(self, name="", timeout=1800): + ThreadLoop.__init__(self, name) + self.timeout = timeout + + def _loop(self): + rlog(0, 'threadsleeper', 'starting threadloop') + self.running = True + + while not self.stopped: + time.sleep(self.timeout) + + if self.stopped: + break + rlog(-1, self.name, 'running') + self.handle() + + self.running = False + rlog(0, 'threadsleeper', 'stopping threadloop') --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/bot.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/bot.py @@ -0,0 +1,1125 @@ +# gozerbot/xmpp/bot.py +# +# + +""" jabber bot definition """ + +__copyright__ = 'this file is in the public domain' + +## IMPORT SECTION + +# gozerbot imports +from gozerbot.users import users +from gozerbot.utils.log import rlog +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.trace import whichmodule +from gozerbot.utils.locking import lockdec +from gozerbot.utils.generic import waitforqueue, toenc, fromenc, jabberstrip, getrandomnick +from gozerbot.config import config +from gozerbot.persist.pdod import Pdod +from gozerbot.utils.dol import Dol +from gozerbot.datadir import datadir +from gozerbot.channels import Channels +from gozerbot.less import Less +from gozerbot.ignore import shouldignore +from gozerbot.callbacks import jcallbacks +from gozerbot.threads.thr import start_new_thread +from gozerbot.fleet import fleet +from gozerbot.botbase import BotBase + +# xmpp imports +from presence import Presence +from message import Message +from iq import Iq +from core import XMLStream +from monitor import xmppmonitor +from wait import XMPPWait, XMPPErrorWait +from jid import JID, InvalidJID + +# basic imports +import time, Queue, os, threading, thread, types, xml, re, hashlib + +## END IMPORT + +## LOCK SECTION + +# locks +outlock = thread.allocate_lock() +inlock = thread.allocate_lock() +connectlock = thread.allocate_lock() +outlocked = lockdec(outlock) +inlocked = lockdec(inlock) +connectlocked = lockdec(connectlock) + +## END LOCK + +class Bot(XMLStream, BotBase): + + """ + xmpp bot class. + + :param name: name of the xmpp bot + :type name: string + :param cfg: config file for the bot + :type config: gozerbot.config.Config + :rtype: None + + """ + + def __init__(self, name, cfg={}): + BotBase.__init__(self, name, cfg) + + if not self.port: + self.port = 5222 + + if not self.host: + raise Exception("host not set in %s xmpp bot" % self.name) + + self.username = self.user.split('@')[0] + XMLStream.__init__(self, self.host, self.port, self.name) + self.type = 'xmpp' + self.outqueue = Queue.Queue() + self.sock = None + self.me = self.user + self.jid = self.me + self.lastin = None + self.test = 0 + self.connecttime = 0 + self.connection = None + self.privwait = XMPPWait() + self.errorwait = XMPPErrorWait() + self.monitor = xmppmonitor + self.jabber = True + self.connectok = threading.Event() + self.jids = {} + self.topics = {} + self.timejoined = {} + self.channels409 = [] + + if not self.state.has_key('ratelimit'): + self.state['ratelimit'] = 0.05 + + if self.port == 0: + self.port = 5222 + + self.monitor.start() + + def _resumedata(self): + + """ + return data needed for resuming. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot._resumedata + + """ + + return {self.name: [self.server, self.user, self.password, self.port]} + + def _outputloop(self): + + """ + loop to take care of output to the server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot._outputloop + + """ + + rlog(10, self.name, 'starting outputloop') + lastsend = time.time() + charssend = 0 + + while not self.stopped: + time.sleep(0.01) + what = self.outqueue.get() + + if self.stopped or what == None: + break + + if charssend + len(what) < 1000: + + try: + self._raw(what) + except Exception, ex: + self.error = str(ex) + handle_exception + continue + + lastsend = time.time() + charssend += len(what) + + else: + + if time.time() - lastsend > 1: + + try: + self._raw(what) + except Exception, ex: + handle_exception() + continue + + lastsend = time.time() + charssend = len(what) + continue + + charssend = 0 + sleeptime = self.cfg['jabberoutsleep'] or config['jabberoutsleep'] + + if not sleeptime: + sleeptime = 1 + + rlog(10, self.name, 'jabberoutsleep .. sleeping %s seconds' % sleeptime) + time.sleep(sleeptime) + + try: + self._raw(what) + except Exception, ex: + handle_exception() + + rlog(10, self.name, 'stopping outputloop .. %s' % (self.error or 'no error set')) + + def _keepalive(self): + + """ + keepalive method .. send empty string to self every 3 minutes. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot._keepalive + + """ + + nrsec = 0 + + + + while not self.stopped: + time.sleep(1) + nrsec += 1 + + if nrsec < 180: + continue + else: + nrsec = 0 + + self.sendpresence() + + def sendpresence(self): + """ + send presence based on status and status text + set by user + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.sendpresence + + """ + + if self.state.has_key('status') and self.state['status']: + status = self.state['status'] + else: + status = "" + if self.state.has_key('show') and self.state['show']: + show = self.state['show'] + else: + show = "" + rlog(4, self.name, 'keepalive: %s - %s' % (show, status)) + if show and status: + p = Presence({'to': self.me, 'show': show, 'status': status}, self) + elif show: + p = Presence({'to': self.me, 'show': show }, self) + elif status: + p = Presence({'to': self.me, 'status': status}, self) + else: + p = Presence({'to': self.me }, self) + + self.send(p) + + def _keepchannelsalive(self): + + """ + channels keep alive method. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot._keepchannelsalive + + """ + + nrsec = 0 + p = Presence({'to': self.me, 'txt': '' }, self) + + while not self.stopped: + time.sleep(1) + nrsec += 1 + + if nrsec < 600: + continue + else: + nrsec = 0 + + for chan in self.state['joinedchannels']: + + if chan not in self.channels409: + p = Presence({'to': chan}, self) + self.send(p) + + def connect(self, reconnect=True): + + """ + connect the xmpp server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.connect + + """ + + + if self.stopped: + rlog(100, self.name, 'bot is stopped .. not connecting') + return + try: + if not XMLStream.connect(self): + rlog(10, self.name, 'connect to %s:%s failed' % (self.host, self.port)) + return + else: + rlog(10, self.name, 'connected') + self.logon(self.user, self.password) + start_new_thread(self._outputloop, ()) + start_new_thread(self._keepalive, ()) + #start_new_thread(self._keepchannelsalive, ()) + self.requestroster() + self._raw("") + self.connectok.set() + self.sock.settimeout(None) + return True + except Exception, ex: + handle_exception() + if reconnect: + self.reconnect() + + def logon(self, user, password): + + """ + logon the xmpp server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.logon + + """ + + iq = self.initstream() + if not self.auth(user, password, iq.id): + if self.register(user, password): + time.sleep(5) + self.auth(user, password) + else: + self.exit() + return + XMLStream.logon(self) + + def initstream(self): + + """ + send initial string sequence to the xmpp server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.initstream + + """ + + + rlog(10, self.name, 'starting initial stream sequence') + self._raw("""""" % (self.user.split('@')[1], )) + result = self.connection.read() + iq = self.loop_one(result) + rlog(3, self.name, "initstream: %s" % result) + return iq + + def register(self, jid, password): + + """ + register the jid to the server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.register + + """ + + try: + resource = jid.split("/")[1] + except IndexError: + resource = "gozerbot" + rlog(10, self.name, 'registering %s' % jid) + self._raw("""%s""" % jid.split("@")[0]) + #self._raw("""""") + result = self.connection.read() + #rlog(3, self.name, 'register: %s' % result) + iq = self.loop_one(result) + if not iq: + rlog(10, self.name, "unable to register") + return + rlog(3, self.name, 'register: %s' % str(iq)) + self._raw("""%s%s%s""" % (jid.split('@')[0], resource, password)) + result = self.connection.read() + rlog(3, self.name, 'register: %s' % result) + if not result: + return False + iq = self.loop_one(result) + if not iq: + rlog(10, self.name, "can't decode data: %s" % result) + return False + rlog(3, self.name, 'register: %s' % result) + if iq.error: + rlog(10, self.name, 'REGISTER FAILED: %s' % iq.error) + if iq.error.code == "405": + rlog(10, self.name, "this server doesn't allow registration by the bot, you need to create an account for it yourself") + else: + rlog(10, self.name, result) + self.error = iq.error + return False + rlog(10, self.name, 'register ok') + return True + + def auth(self, jid, password, digest=""): + + """ + auth against the xmpp server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.auth + + """ + + rlog(10, self.name, 'authing %s' % jid) + name = jid.split('@')[0] + rsrc = self.cfg['resource'] or config['resource'] or 'gozerbot'; + self._raw("""%s""" % name) + result = self.connection.read() + iq = self.loop_one(result) + rlog(3, self.name, 'auth:' + result) + if ('digest' in result) and digest: + s = hashlib.new('SHA1') + s.update(digest) + s.update(password) + d = s.hexdigest() + self._raw("""%s%s%s""" % (name, d, rsrc)) + else: + self._raw("""%s%s%s""" % (name, rsrc, password)) + result = self.connection.read() + iq = self.loop_one(result) + if not iq: + rlog(10, self.name, 'auth failed: %s' % result) + return False + rlog(3, self.name, 'auth: ' + result) + if iq.error: + rlog(10, self.name, 'AUTH FAILED: %s' % iq.error) + if iq.error.code == "401": + rlog(10, self.name, "wrong user or password (or user not known)") + else: + rlog(10, self.name, result) + self.error = iq.error + return False + rlog(10, self.name, 'auth ok') + return True + + def requestroster(self): + + """ + request roster from xmpp server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.requestroster + + """ + + self._raw("") + + def disconnectHandler(self, ex): + + """ + disconnect handler. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.disconnectHandler + + """ + + rlog(100, self.name, "disconnected: %s" % str(ex)) + + if not self.stopped: + self.reconnect() + else: + self.exit() + + def joinchannels(self): + + """ + join all already joined channels. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.joinchannels + + """ + + + for i in self.state['joinedchannels']: + key = self.channels.getkey(i) + nick = self.channels.getnick(i) + result = self.join(i, key, nick) + if result == 1: + rlog(10, self.name, 'joined %s' % i) + else: + rlog(10, self.name, 'failed to join %s: %s' % (i, result)) + + def broadcast(self, txt): + + """ + broadcast txt to all joined channels. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.broadcast + + """ + + for i in self.state['joinedchannels']: + self.say(i, txt) + + def handle_iq(self, data): + + """ + iq handler .. overload this when needed. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.handle_iq + + """ + + pass + + def handle_message(self, data): + + """ + message handler. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.handle_message + + """ + + if self.test: + return + + m = Message(data, self) + + if data.type == 'groupchat' and data.subject: + self.topiccheck(m) + nm = Message(m, bot=self) + jcallbacks.check(self, nm) + return + + if data.get('x').xmlns == 'jabber:x:delay': + rlog(5, self.name, "ignoring delayed message") + return + + self.privwait.check(m) + + if m.isresponse: + return + + if m.groupchat: + m.msg = False + + jid = None + m.origjid = m.jid + + for node in m.subelements: + try: + m.jid = node.x.item.jid + except (AttributeError, TypeError): + continue + + #if not m.txt: + # return + if self.me in m.fromm: + return 0 + + #if m.groupchat and self.nick == m.resource: + # return 0 + go = 0 + + try: + cc = self.channels[m.channel]['cc'] + except (TypeError, KeyError): + cc = config['defaultcc'] or '!' + + try: + channick = self.channels[m.channel]['nick'] + except (TypeError, KeyError): + channick = self.nick + + if m.msg and not config['noccinmsg']: + go = 1 + if not m.txt: + go = 0 + elif m.txt[0] in cc: + go = 1 + elif m.txt.startswith("%s: " % channick): + m.txt = m.txt.replace("%s: " % channick, "") + go = 1 + elif m.txt.startswith("%s, " % channick): + m.txt = m.txt.replace("%s, " % channick, "") + go = 1 + + if m.txt and m.txt[0] in cc: + m.txt = m.txt[1:] + + if go: + try: + from gozerbot.plugins import plugins + if plugins.woulddispatch(self, m): + m.usercmnd = True + plugins.trydispatch(self, m) + except: + handle_exception() + + try: + if m.type == 'error': + if m.code: + rlog(10, self.name + '.error', str(m)) + self.errorwait.check(m) + self.errorHandler(m) + except Exception, ex: + handle_exception() + + mm = Message(m, self) + jcallbacks.check(self, mm) + + def errorHandler(self, event): + + """ + error handler .. calls the errorhandler set in the event. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.errorHandler + + """ + + try: + event.errorHandler() + except AttributeError: + rlog(10, self.name, 'unhandled error: %s' % event) + + def handle_presence(self, data): + + """ + presence handler. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.handle_presence + + """ + + p = Presence(data, self) + frm = p.fromm + nickk = "" + nick = p.nick + + if self.me in p.userhost: + return 0 + + if nick: + self.userhosts.data[nick] = str(frm) + nickk = nick + + jid = None + + for node in p.subelements: + try: + jid = node.x.item.jid + except (AttributeError, TypeError): + continue + + if nickk and jid: + channel = p.channel + if not self.jids.has_key(channel): + self.jids[channel] = {} + self.jids[channel][nickk] = jid + self.userhosts.data[nickk] = str(jid) + rlog(0, self.name, 'setting jid of %s (%s) to %s' % (nickk, \ + channel, jid)) + + if p.type == 'subscribe': + pres = Presence({'to': p.fromm, 'type': 'subscribed'}, self) + self.send(pres) + pres = Presence({'to': p.fromm, 'type': 'subscribe'}, self) + self.send(pres) + + nick = p.resource + + if p.type != 'unavailable': + self.userchannels.adduniq(nick, p.channel) + p.joined = True + p.type = 'available' + elif self.me in p.userhost: + try: + del self.jids[p.channel] + rlog(0, self.name, 'removed %s channel jids' % p.channel) + except KeyError: + pass + else: + try: + del self.jids[p.channel][p.nick] + rlog(0, self.name, 'removed %s jid' % p.nick) + except KeyError: + pass + + pp = Presence(p, self) + jcallbacks.check(self, pp) + + if p.type == 'error': + for node in p.subelements: + try: + err = node.error.code + except (AttributeError, TypeError): + err = 'no error set' + try: + txt = node.text.data + except (AttributeError, TypeError): + txt = "" + if err: + rlog(10, self.name + '.error', "%s: %s" % (err, txt)) + + self.errorwait.check(p) + + try: + method = getattr(self,'handle_' + err) + # try to call method + try: + method(p) + except: + handle_exception() + except AttributeError: + # no command method to handle event + pass + + def reconnect(self): + + """ + reconnect to the server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.reconnect + + """ + + if self.stopped: + rlog(100, self.name, 'bot is stopped .. not reconnecting') + return + + rlog(100, self.name, 'reconnecting .. sleeping 15 seconds') + self.exit() + time.sleep(15) + newbot = Bot(self.name, self.cfg) + + if newbot.connect(): + self.name += '.old' + newbot.joinchannels() + fleet.replace(self.name, newbot) + return True + + return False + + + def send(self, what): + + """ + send stanza to the server. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.send + + """ + + try: + to = what['to'] + except (KeyError, TypeError): + rlog(10, self.name, "can't determine where to send %s to" % what) + return + + try: + jid = JID(to) + except (InvalidJID, AttributeError): + rlog(10, self.name, "invalid jid %s .. %s" % (str(to), str(what))) + return + + try: + del what['from'] + #del what['fromm'] + except KeyError: + pass + + try: + xml = what.toxml() + if not xml: + raise Exception("can't convert %s to xml .. bot.send()" % what) + except (AttributeError, TypeError): + handle_exception() + return + #raise Exception("can't convert %s to xml .. bot.send()" % what) + + self.outqueue.put(toenc(xml)) + self.monitor.put(self, what) + + def sendnocb(self, what): + + """ + send to server without calling callbacks/monitors. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.sendnocb + + """ + + try: + xml = what.toxml() + except AttributeError: + xml = what + self.outqueue.put(toenc(xml)) + + def action(self, printto, txt, fromm=None, groupchat=True): + + """ + send an action. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.action + + """ + + txt = "/me " + txt + + if self.google: + fromm = self.me + + if printto in self.state['joinedchannels'] and groupchat: + message = Message({'to': printto, 'txt': txt, 'type': 'groupchat'}, self) + else: + message = Message({'to': printto, 'txt': txt}, self) + + if fromm: + message.fromm = fromm + + self.send(message) + + def say(self, printto, txt, fromm=None, groupchat=True, speed=5, type="normal", how=''): + + """ + say txt to channel/JID. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.say + + """ + + txt = jabberstrip(txt) + + if self.google: + fromm = self.me + + if printto in self.state['joinedchannels'] and groupchat: + message = Message({'to': printto, 'body': txt, 'type': 'groupchat'}, self) + else: + message = Message({'to': printto, 'body': txt, 'type': 'chat'}, self) + + if fromm: + message.fromm = fromm + else: + message.fromm = self.me + + self.send(message) + + def saynocb(self, printto, txt, fromm=None, groupchat=True, speed=5, type="normal", how=''): + + """ + say txt to channel/JID without calling callbacks/monitors. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.saynocb + + """ + + txt = jabberstrip(txt) + + #if self.google: + # fromm = self.me + + if printto in self.state['joinedchannels'] and groupchat: + message = Message({'to': printto, 'body': txt, 'type': 'groupchat'}, self) + else: + message = Message({'to': printto, 'body': txt}, self) + + if fromm: + message.fromm = fromm + else: + message.fromm = self.me + + self.send(message) + + def userwait(self, msg, txt): + + """ + wait for user response. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.userwait + + """ + + msg.reply(txt) + queue = Queue.Queue() + self.privwait.register(msg, queue) + result = queue.get() + + if result: + return result.txt + + def save(self): + + """ + save bot's state. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.save + + """ + + self.state.save() + + def quit(self): + + """ + send unavailable presence. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.quit + + """ + + presence = Presence({'type': 'unavailable'}, self) + + for i in self.state['joinedchannels']: + presence.to = i + self.send(presence) + + presence = Presence({'type': 'unavailable'}, self) + presence['from'] = self.me + self.send(presence) + time.sleep(1) + + def exit(self): + + """ + exit the bot. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.exit + + """ + + self.quit() + self.stopped = 1 + self.outqueue.put_nowait(None) + self.save() + time.sleep(3) + rlog(10, self.name, 'exit') + + def join(self, channel, password=None, nick=None): + + """ + join conference. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.join + + """ + + if '#' in channel: + return + + try: + if not nick: + nick = channel.split('/')[1] + except IndexError: + nick = self.nick + + channel = channel.split('/')[0] + + if not self.channels.has_key(channel): + # init channel data + self.channels.setdefault(channel, {}) + + # setup error wait + q = Queue.Queue() + self.errorwait.register("409", q, 3) + self.errorwait.register("401", q, 3) + self.errorwait.register("400", q, 3) + + # do the actual join + presence = Presence({'to': channel + '/' + nick}, self) + + if password: + presence.x.password = password +# passnode = Node('password') +# passnode.addData(password) +# presence.addChild(name='x', namespace='http://jabber.org/protocol/muc', \ +#payload=[passnode, ]) + + self.send(presence) + errorobj = waitforqueue(q, 3) + + if errorobj: + err = errorobj[0].error + rlog(100, self.name, 'error joining %s: %s' % (channel, err)) + if err >= '400': + if channel not in self.channels409: + self.channels409.append(channel) + return err + + self.timejoined[channel] = time.time() + chan = self.channels[channel] + # if password is provided set it + chan['nick'] = nick + + if password: + chan['key'] = password + + # check for control char .. if its not there init to ! + if not chan.has_key('cc'): + chan['cc'] = config['defaultcc'] or '!' + + if not chan.has_key('perms'): + chan['perms'] = [] + + self.channels.save() + + if channel not in self.state['joinedchannels']: + self.state['joinedchannels'].append(channel) + + if channel in self.channels409: + self.channels409.remove(channel) + + self.state.save() + return 1 + + def part(self, channel): + + """ + leave conference. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.part + + """ + + if '#' in channel: + return + + presence = Presence({'to': channel}, self) + presence.type = 'unavailable' + self.send(presence) + + if channel in self.state['joinedchannels']: + self.state['joinedchannels'].remove(channel) + + self.state.save() + return 1 + + def outputnolog(self, printto, what, how, who=None, fromm=None): + + """ + do output but don't log it. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.outputnolog + + """ + + if fromm and shouldignore(fromm): + return + + self.saynocb(printto, what) + + def topiccheck(self, msg): + + """ + check if topic is set. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.topiccheck + + """ + + if msg.groupchat: + try: + topic = msg.subject + if not topic: + return None + self.topics[msg.channel] = (topic, msg.userhost, time.time()) + rlog(0, self.name, 'topic of %s set to %s' % \ +(msg.channel, topic)) + except AttributeError: + return None + + def settopic(self, channel, txt): + + """ + set topic. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.settopic + + """ + + pres = Message({'to': channel, 'subject': txt}, self) + pres.type = 'groupchat' + self.send(pres) + + def gettopic(self, channel): + + """ + get topic. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.gettopic + + """ + + try: + topic = self.topics[channel] + return topic + except KeyError: + return None + + def domsg(self, msg): + + """ + dispatch an msg on the bot. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.domsg + + """ + + from gozerbot.plugins import plugins + plugins.trydispatch(self, msg) + + def sendraw(self, data): + + """ + compat function. + + .. literalinclude:: ../../../gozerbot/xmpp/bot.py + :pyobject: Bot.sendraw + + """ + + self._raw(data) --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/monitor.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/monitor.py @@ -0,0 +1,41 @@ +# gozerbot/xmpp/monitor.py +# +# + +""" monitors .. call callback on bot output. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.exception import handle_exception +from gozerbot.monitor import Monitor +from gozerbot.config import config +from gozerbot.eventbase import EventBase +from message import Message + +class XMPPmonitor(Monitor): + + """ monitor jabber output """ + + def handle(self, bot, event): + + """ fire jabber monitor callbacks. """ + + try: + #event.fromm = event['from'] = event.to + e = Message() + e.copyin(event) + e.isreply = True + e.iscmnd = True + e.remotecmnd = True + e.remoteout = bot.jid + e.channel = event.to + e.fromm = e['from'] = bot.jid + e.nick = bot.nick + e.botoutput = True + Monitor.handle(self, bot, e) + except AttributeError: + handle_exception() + +# xmpp monitor +xmppmonitor = XMPPmonitor('xmppmonitor') --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/core.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/core.py @@ -0,0 +1,677 @@ +# gozerbot/xmpp/core.py +# +# + +""" + this module contains the core xmpp handling functions. + +""" + + +## IMPORT SECTION + +# gozerbot imports +from gozerbot.datadir import datadir +from gozerbot.config import Config, config +from gozerbot.utils.generic import toenc, jabberstrip +from gozerbot.utils.log import rlog +from gozerbot.utils.lazydict import LazyDict +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.locking import lockdec +from gozerbot.threads.thr import start_new_thread +from gozerbot.utils.trace import whichmodule + +# dom imports +from gozerbot.contrib.xmlstream import NodeBuilder, XMLescape + +# for exceptions +import xml.parsers.expat + +from namespace import attributes, subelements + +# basic imports +import socket, os, time, copy, logging, thread + +## END IMPORT + +## LOCK SECTION + +# locks +outlock = thread.allocate_lock() +inlock = thread.allocate_lock() +connectlock = thread.allocate_lock() +outlocked = lockdec(outlock) +inlocked = lockdec(inlock) +connectlocked = lockdec(connectlock) + +## END LOCK + +class XMLDict(LazyDict): + + + """ + dictionairy to store xml stanza attributes. + + :params input: optional dict to init with + :type input: dict + + """ + + def __init__(self, input={}): + + if input == None: + LazyDict.__init__(self) + else: + LazyDict.__init__(self, input) + + try: + self['fromm'] = self['from'] + except (KeyError, TypeError): + self['fromm'] = '' + + def __getattr__(self, name): + + """ + override getattribute so nodes in payload can be accessed. + + """ + + if not self.has_key(name) and self.has_key('subelements'): + + for i in self['subelements']: + + if name in i: + return i[name] + + return LazyDict.__getattr__(self, name, default="") + + def get(self, name): + + """ + get a attribute by name. + + :param name: name of the attribute + :type name: string + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLDict.get + + """ + + if self.has_key('subelements'): + + for i in self['subelements']: + + if name in i: + + return i[name] + + if self.has_key(name): + + return self[name] + + return LazyDict() + + def toxml(self): + + """ + convert the dictionary to xml. + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLDict.toxml + """ + + res = dict(self) + + if not res: + raise Exception("%s .. toxml() can't convert empty dict" % self.name) + + rlog(-1, self.name, u"toxml input: %s" % res) + elem = self['element'] + main = "<%s" % self['element'] + + for attribute in attributes[elem]: + + if attribute in res: + + if res[attribute]: + main += u" %s='%s'" % (attribute, XMLescape(res[attribute])) + + continue + + main += ">" + gotsub = False + + if res.has_key('txt'): + + if res['txt']: + + main += u"%s" % XMLescape(res['txt']) + gotsub = True + + for subelement in subelements[elem]: + + try: + data = res[subelement] + + if data: + main += "<%s>%s" % (subelement, XMLescape(data), subelement) + gotsub = True + + except KeyError: + pass + + if gotsub: + + main += "" % elem + + else: + + main = main[:-1] + main += "/>" + + return main + + def str(self): + + """ + convert to string. + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLDict.str + """ + + result = "" + elem = self['element'] + for item, value in dict(self).iteritems(): + if item in attributes[elem] or item in subelements[elem] or item == 'txt': + result += "%s='%s' " % (item, value) + return result + +class XMLStream(NodeBuilder): + + + """ + XMLStream. + + :param host: host to connect to + :type host: string + :param port: port to connect to + :type port: int + :param name: name of the xmlstream + :type name: string + + """ + + def __init__(self, host, port, name='gozerxmpp'): + # start sets these + self.name = name + # the connection + self.connection = None + self.stop = False + # parse state + self.result = LazyDict() + self.final = LazyDict() + self.subelements = [] + self.reslist = [] + self.cur = u"" + self.tags = [] + self.host = host + self.port = port + # handlers + self.handlers = LazyDict() + self.addHandler('message', self.handle_message) + self.addHandler('presence', self.handle_presence) + self.addHandler('iq', self.handle_iq) + self.addHandler('stream:stream', self.handle_stream) + self.addHandler('stream:error', self.handle_streamerror) + self.addHandler('stream:features', self.handle_streamfeatures) + + def handle_stream(self, data): + + """ + default stream error handler. + + """ + + rlog(2, self.name, "STREAM: %s" % data) + + def handle_streamerror(self, data): + + """ + default stream error handler. + + """ + + rlog(10, self.name, "STREAMERROR: %s" % data) + + def handle_streamfeatures(self, data): + + """ + default stream error handler. + + """ + + rlog(2, self.name, "STREAMFEATURES: %s" % data) + + def addHandler(self, namespace, func): + + """ + add a namespace handler. + + :param namespace: namespace to register handler for + :type namespace: string + :param func: handler function + :type func: function or method + + """ + + self.handlers[namespace] = func + + def delHandler(self, namespace): + + """ + delete a namespace handler. + + :param namespace: namespace to delete handler for + :type namespace: string + + """ + + del self.handlers[namespace] + + def getHandler(self, namespace): + + """ + get a namespace handler. + + :param namespace: namespace get handler for + :type namespace: string + + """ + + try: + return self.handlers[namespace] + except KeyError: + return None + + def loop_one(self, data): + + """ + handle one xml stanza. + + :param data: data as received on the socket + :type data: string + :rtype: gozerbot.utils.lazydict.LazyDict + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream.loop_one + + """ + + NodeBuilder.__init__(self) + self._dispatch_depth = 2 + + try: + self._parser.Parse(data.strip()) + + except xml.parsers.expat.ExpatError, ex: + if 'not well-formed' in str(ex): + rlog(10, self.name, "data is not well formed: %s" % str(data)) + return {} + rlog(10, self.name, "ALERT: %s - %s" % (str(ex), data)) + #raise + except Exception, ex: + handle_exception() + return {} + + return self.finish(data) + + @inlocked + def _doprocess(self): + + """ + proces all incoming data. + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream._doprocess + """ + + rlog(10, self.name, 'starting readloop') + self.buffer = "" + + while not self.stopped: + + try: + data = self.connection.read() + + if data == "": + rlog(10, self.name, 'remote disconnected') + self.error = 'disconnected' + self.disconnectHandler(Exception('remote %s disconnected' % self.host)) + break + + if data: + self.buffer += data + else: + continue + + self.buffer = self.buffer.strip() + + rlog(6, self.name, u'trying: %s' % self.buffer) + + #if self.buffer.endswith('') or self.buffer.endswith('') or self.buffer.endswith(''): + + if not self.loop_one(self.buffer): + rlog(10, self.name, 'failed to process %s' % self.buffer) + else: + self.buffer = "" + + except xml.parsers.expat.ExpatError, ex: + rlog(10, self.name, u"%s: %s" % (str(ex), data)) + self.buffer = "" + self.error = str(ex) + self.disconnectHandler(ex) + break + + except Exception, ex: + handle_exception() + self.error = str(ex) + self.disconnectHandler(ex) + break + + rlog(10, self.name, 'stopping readloop .. %s' % (self.error or 'error not set')) + + def _raw(self, stanza): + + """ + output a xml stanza to the socket. + + :param stanza: stanza to send to the socket + :type stanza: string + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream._raw + + """ + + try: + + if self.stopped: + rlog(10, self.name, 'bot is stopped .. not sending') + return + + what = toenc(jabberstrip(stanza.strip()), self.encoding) + + if not what.endswith('>') or not what.startswith('<'): + rlog(100, self.name, 'invalid stanza: %s' % what) + return + + if what.startswith('\r\n' % self.user.split('@')[1]) + time.sleep(3) + result = self.sock.recv(1500) + rlog(3, self.name, result) + #iq = self.loop_one(result) + #self.id = iq.id + self.sock.send('\r\n') + time.sleep(3) + rlog(3, self.name, self.sock.recv(1500)) + self.sock.settimeout(60) + return self.dossl() + + def dossl(self): + + try: + import ssl + self.connection = ssl.wrap_socket(self.sock) + except ImportError: + self.connection = socket.ssl(self.sock) + + if self.connection: + return True + else: + return False + + def logon(self): + """ + called upon logon on the server. + + """ + + start_new_thread(self._doprocess, ()) + + def finish(self, data): + + """ + finish processing of an xml stanza. + + :param data: data which has been used to process the data + :type data: string + :rtype: gozerbot.utils.lazydict.LazyDict + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream.finish + + """ + + self.final['subelements'] = self.subelements + + if self.tags: + element = self.tags[0] + rlog(3, self.name, "setting element: %s" % element) + else: + element = 'presence' + + self.final['element'] = element + method = self.getHandler(element) + + if method: + + try: + result = XMLDict(self.final) + result.bot = self + result.orig = data + result.jabber = True + method(result) + + except Exception, ex: + handle_exception() + result = {} + + else: + rlog(10, self.name, "can't find handler for %s" % element) + result = {} + + self.final = {} + self.reslist = [] + self.tags = [] + self.subelements = [] + return result + + def unknown_starttag(self, tag, attrs): + + """ + handler called by the self._parser on start of a unknown start tag. + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream.unknown_starttag + + """ + + NodeBuilder.unknown_starttag(self, tag, attrs) + self.cur = tag + if not self.tags: + self.final.update(attrs) + else: + self.result[tag] = attrs + self.tags.append(tag) + + def unknown_endtag(self, tag): + + """ + handler called by the self._parser on start of a unknown endtag. + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream.unknown_endtag + + """ + + NodeBuilder.unknown_endtag(self, tag) + self.result = {} + self.cur = u"" + + def handle_data(self, data): + + """ + node data handler. + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream.handle_data + + """ + + NodeBuilder.handle_data(self, data) + + def dispatch(self, dom): + + """ + dispatch a dom to the appropiate handler. + + :param dom: the dom to be dispatched + + .. literalinclude:: ../../../gozerbot/xmpp/core.py + :pyobject: XMLStream.dispatch + + """ + + res = LazyDict() + parentname = dom.getName() + data = dom.getData() + + if data: + self.final[parentname] = data + + if parentname == 'body': + self.final['txt'] = data + + attrs = dom.getAttributes() + ns = dom.getNamespace() + res[parentname] = LazyDict() + res[parentname]['data'] = data + res[parentname].update(attrs) + + if ns: + res[parentname]['xmlns'] = ns + + for child in dom.getChildren(): + name = child.getName() + data = child.getData() + + if data: + self.final[name] = data + + attrs = child.getAttributes() + ns = child.getNamespace() + res[parentname][name] = LazyDict() + res[parentname][name]['data'] = data + res[parentname][name].update(attrs) + self.final.update(attrs) + + if ns: + res[parentname][name]['xmlns'] = ns + + self.subelements.append(res) + + def exit(self): + + """ + stop the stream handling. + + """ + + self.stopped = True + + + def reconnect(self): + + """ + reconnect to the server. + + """ + + rlog(10, self.name, 'reconnect') + self.exit() + rlog(10, self.name, 'sleeping 15 seconds') + time.sleep(15) + return self.connect() + + def disconnectHandler(self, ex): + + """ + handler called on disconnect. + + :param ex: exception leading to the disconnect + :type ex: Exception + + """ + + self.stop = True + rlog(10, self.name, 'disconnected: %s' % str(ex)) + self.reconnect() + +## INIT SECTION + +# no vars + +## END INIT --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/wait.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/wait.py @@ -0,0 +1,88 @@ +# gozerbot/xmpp/wait.py +# +# + +""" wait for ircevent based on ircevent.CMND """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec +import gozerbot.threads.thr as thr + +# xmpp imports +from gozerbot.wait import Wait + +# basic imports +import time, thread + +# locks +waitlock = thread.allocate_lock() +locked = lockdec(waitlock) + +class XMPPWait(Wait): + + """ wait object for jabber messages. """ + + def register(self, catch, queue, timeout=15): + + """ register wait for privmsg. """ + + rlog(1, 'xmpp-wait', 'registering for %s' % catch) + self.ticket += 1 + self.waitlist.append((catch, queue, self.ticket)) + if timeout: + thr.start_new_thread(self.dotimeout, (timeout, self.ticket)) + return self.ticket + + def check(self, msg): + + """ check if is waited for. """ + + for teller in range(len(self.waitlist)-1, -1, -1): + i = self.waitlist[teller] + if i[0] == msg.userhost: + msg.ticket = i[2] + i[1].put_nowait(msg) + self.delete(msg.ticket) + rlog(10, 'xmpp-wait', 'got response for %s' % i[0]) + msg.isresponse = 1 + + @locked + def delete(self, ticket): + + """ delete wait item with ticket nr. """ + + for itemnr in range(len(self.waitlist)-1, -1, -1): + item = self.waitlist[itemnr] + if item[2] == ticket: + item[1].put_nowait(None) + try: + del self.waitlist[itemnr] + rlog(1, 'xmpp-wait', 'deleted ' + str(ticket)) + except IndexError: + pass + return 1 + +class XMPPErrorWait(XMPPWait): + + """ wait for jabber errors. """ + + def check(self, msg): + + """ check if is waited for. """ + + if not msg.type == 'error': + return + + errorcode = msg.error + + for teller in range(len(self.waitlist)-1, -1, -1): + i = self.waitlist[teller] + if i[0] == 'ALL' or i[0] == errorcode: + msg.error = msg.error + msg.ticket = i[2] + i[1].put_nowait(msg) + self.delete(msg.ticket) + rlog(10,'xmpp-errorwait','got error response for %s' % i[0]) --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/presence.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/presence.py @@ -0,0 +1,50 @@ +# gozerbot/xmpp/presence.py +# +# + +""" Presence. """ + +# gozerbot imports +from gozerbot.eventbase import EventBase +from gozerbot.utils.trace import whichmodule +from gozerbot.utils.log import rlog + +# xmpp imports +from core import XMLDict + +# basic imports +import time + +class Presence(EventBase): + + def __init__(self, nodedict={}, bot=None): + rlog(2, whichmodule(1), 'PRESENCE: ' + str(nodedict)) + EventBase.__init__(self, nodedict, bot) + self.element = 'presence' + + def toirc(self): + """ set ircevent compatible attributes """ + self.cmnd = 'Presence' + + try: + self.nick = self.fromm.split('/')[1] + except (AttributeError, IndexError): + self.nick = "" + + self.jid = self.jid or self.fromm + self.ruserhost = self.jid + self.userhost = str(self.jid) + self.resource = self.nick + self.stripped = self.jid.split('/')[0] + self.channel = self.fromm.split('/')[0] + self.printto = self.channel + self.origtxt = self.txt + self.time = time.time() + + if self.type == 'groupchat': + self.groupchat = True + else: + self.groupchat = False + + if self.txt: + makeargrest(self) --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/namespace.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/namespace.py @@ -0,0 +1,18 @@ +# gozerbot/xmpp/namespace.py +# +# + +# CONSTANTS + +attributes = {} +subelements = {} + +attributes['message'] = ['type', 'from', 'to', 'id'] +subelements['message'] = ['subject', 'body', 'error', 'html', 'thread', 'x'] + +attributes['presence'] = ['type', 'from', 'to', 'id'] +subelements['presence'] = ['show', 'status', 'priority', 'x'] + + +attributes['iq'] = ['type', 'from', 'to', 'id'] +subelements['iq'] = ['query', 'error'] --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/message.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/message.py @@ -0,0 +1,277 @@ +# gozerbot/xmpp/message.py +# +# + +""" jabber message definition .. types can be normal, chat, groupchat, + headline or error +""" + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.log import rlog +from gozerbot.utils.trace import whichmodule +from gozerbot.utils.generic import toenc, fromenc, jabberstrip, makeargrest +from gozerbot.utils.locking import lockdec +from gozerbot.eventbase import EventBase +from gozerbot.config import config + +# xmpp imports +from core import XMLDict + +# basic imports +import types, time, thread + +# locks +replylock = thread.allocate_lock() +replylocked = lockdec(replylock) + +if config['dotchars']: + dotchars = config['dotchars'] +else: + dotchars = ' .. ' + +class Message(EventBase): + + """ jabber message object. """ + + def __init__(self, nodedict={}, bot=None): + EventBase.__init__(self, nodedict, bot) + self.element = "message" + self.jabber = True + rlog(2, whichmodule(1), 'MESSAGE: ' + str(self)) + + def __copy__(self): + return Message(self, self.bot) + + def __deepcopy__(self, bla): + return Message(self, self.bot) + + + #@replylocked + def reply(self, txt, result=None, nick=None, dot=False, nritems=False, nr=False, fromm=None, private=False): + + """ reply txt. """ + + if result == []: + return + + restxt = "" + + if type(result) == types.DictType: + + for i, j in result.iteritems(): + if type(j) == types.ListType: + try: + z = dotchars.join(j) + except TypeError: + z = unicode(j) + else: + z = j + if dot == True: + restxt += "%s: %s %s " % (i, z, dotchars) + elif dot: + restxt += "%s: %s %s " % (i, z, dot) + else: + restxt += "%s: %s " % (i, z) + + if restxt: + if dot == True: + restxt = restxt[:-6] + elif dot: + restxt = restxt[:-len(dot)] + lt = False + + if type(txt) == types.ListType and not result: + result = txt + origtxt = "" + lt = True + else: + origtxt = txt + + if result: + lt = True + + if self.queues: + for i in self.queues: + if lt: + for j in result: + i.put_nowait(j) + else: + i.put_nowait(txt) + if self.onlyqueues: + return + + pretxt = origtxt + + if lt and not restxt: + res = [] + for i in result: + if type(i) == types.ListType or type(i) == types.TupleType: + try: + res.append(dotchars.join(i)) + except TypeError: + res.append(unicode(i)) + else: + res.append(i) + result = res + if nritems: + if len(txt) > 1: + pretxt = "(%s items) .. " % len(result) + txtlist = [unicode(i) for i in result] + if not nr is False: + try: + start = int(nr) + except ValueError: + start = 0 + txtlist2 = [] + teller = start + for i in txtlist: + txtlist2.append("%s) %s" % (teller, i)) + teller += 1 + txtlist = txtlist2 + if dot == True: + restxt = dotchars.join(txtlist) + elif dot: + restxt = dot.join(txtlist) + else: + restxt = ' '.join(txtlist) + + if pretxt: + restxt = pretxt + restxt + + txt = jabberstrip(restxt) + + if not txt: + return + + what = txt + txtlist = [] + start = 0 + end = 900 + length = len(what) + + for i in range(length/end+1): + endword = what.find(' ', end) + if endword == -1: + endword = end + txtlist.append(what[start:endword]) + start = endword + end = start + 900 + + size = 0 + + # see if we need to store output in less cache + if len(txtlist) > 1: + self.bot.less.add(self.userhost, txtlist) + size = len(txtlist) - 1 + result = txtlist[:1][0] + if size: + result += " (+%s)" % size + else: + result = txtlist[0] + + if self.filtered(result): + return + + try: + to = self.options['--to'] + except KeyError: + to = None + + outtype = self.type + + if to and to in self.bot.state['joinedchannels']: + outtype = 'groupchat' + self.groupchat = True + self.msg = False + + repl = Message({'from': self.me, 'to': to or self.jid, 'type': outtype, 'txt': result}) + + if self.groupchat: + if self.resource == self.bot.nick: + return + if to: + pass + elif nick: + repl.type = 'chat' + elif private: + repl.to = self.userhost + elif self.printto: + repl.to = self.printto + else: + repl.to = self.channel + + #repl['from'] = self.bot.me + + if nick: + repl.type = 'normal' + elif not repl.type: + repl.type = 'chat' + + self['bot'].send(repl) + + def toirc(self): + + """ set ircevent compat attributes. """ + + self.jidchange = False + self.cmnd = 'Message' + + try: + self.resource = self.fromm.split('/')[1] + except IndexError: + pass + + self.channel = self['fromm'].split('/')[0] + self.origchannel = self.channel + self.nick = self.resource + + try: + if self.bot: + self.jid = self.bot.jids[self.channel][self.resource] + self.jidchange = True + else: + self.jid = self.fromm + except KeyError: + self.jid = self.fromm + + self.ruserhost = self.jid + self.userhost = self.jid + self.stripped = self.jid.split('/')[0] + self.printto = self.channel + + for node in self.subelements: + try: + self.txt = node.body.data + except (AttributeError, ValueError): + continue + self.origtxt = self.txt + self.time = time.time() + + if self.type == 'groupchat': + self.groupchat = True + if self.jidchange: + self.userhost = self.stripped + else: + self.groupchat = False + self.userhost = self.stripped + + self.msg = not self.groupchat + + def errorHandler(self): + try: + code = self.get('error').code + except Exception, ex: + handle_exception() + try: + method = getattr(self, "handle_%s" % code) + if method: + rlog(10, 'message', 'dispatching error to handler %s' % str(method)) + method(self) + except AttributeError, ex: + rlog(10, 'message', 'unhandled error %s' % code) + except: + handle_exception() + --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/__init__.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/__init__.py @@ -0,0 +1 @@ + --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/iq.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/iq.py @@ -0,0 +1,56 @@ +# gozerbot/xmpp/presence.py +# +# + +""" Iq. """ + +# gozerbot imports +from gozerbot.eventbase import EventBase +from gozerbot.utils.trace import whichmodule +from gozerbot.utils.log import rlog + +# xmpp imports +from core import XMLDict + +# basic imports +import time + +class Iq(EventBase): + + def __init__(self, nodedict={}, bot=None): + rlog(2, whichmodule(1), 'IQ: ' + str(nodedict)) + EventBase.__init__(self, nodedict, bot) + self['element'] = 'iq' + + def toirc(self): + """ set ircevent compatible attributes """ + self.cmnd = 'Iq' + self.conn = None + self.arguments = [] + + try: + self.nick = self.fromm.split('/')[1] + except (AttributeError, IndexError): + pass + + self.jid = self.jid or self.fromm + self.ruserhost = self.jid + self.userhost = str(self.jid) + self.resource = self.nick + self.stripped = self.jid.split('/')[0] + self.channel = self.fromm.split('/')[0] + self.printto = self.channel + self.origtxt = self.txt + self.time = time.time() + self.msg = None + self.rest = ' '.join(self.args) + self.sock = None + self.speed = 5 + if self.type == 'groupchat': + self.groupchat = True + else: + self.groupchat = False + if self.txt: + makeargrest(self) + self.joined = False + self.denied = False --- gozerbot-0.99.1.orig/build/lib/gozerbot/xmpp/jid.py +++ gozerbot-0.99.1/build/lib/gozerbot/xmpp/jid.py @@ -0,0 +1,36 @@ +# gozerbot/xmpp/JID.py +# +# + +class InvalidJID(BaseException): + + pass + +class JID(object): + + def __init__(self, str): + if not str: + self.full = "" + self.user = "" + self.userhost = "" + self.host = "" + self.resource = "" + return + if not self.validate(str): + raise InvalidJID(str) + self.full = str + self.user = self.full.split('@')[0] + self.userhost = self.full.split('/')[0] + try: + self.host = self.userhost.split('@')[1] + except (IndexError, ValueError): + raise InvalidJID(str) + + try: + self.resource = self.full.split('/')[1] + except (IndexError, ValueError): + self.resource = u"" + + def validate(self, s): + if not '#' in s: + return True --- gozerbot-0.99.1.orig/build/lib/gozerbot/persist/persistconfig.py +++ gozerbot-0.99.1/build/lib/gozerbot/persist/persistconfig.py @@ -0,0 +1,294 @@ +# gozerbot/persistconfig.py +# +# + +""" allow data to be pickled to disk .. creating the persisted object + restores data. + +usage: + !plug-cfg -> shows list of all config + !plug-cfg key value -> sets value to key + !plug-cfg key -> shows list of key + !plug-cfg key add value -> adds value to list + !plug-cfg key remove value -> removes value from list + !plug-cfg key clear -> clears entire list + !plug-cfgsave -> force save configuration to disk + +todo: + - plugin api (more work needed?) + +""" + +__copyright__ = 'this file is in the public domain' +__author__ = 'Bas van Oostveen' + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.utils.trace import calledfrom +from gozerbot.commands import cmnds, Command +from gozerbot.examples import examples +from gozerbot.datadir import datadir +from gozerbot.persist.persist import Persist +from gozerbot.config import Config, config + +# basic imports +import sys, os, types, time + +class PersistConfigError(Exception): pass + +class PersistConfig(Config): + + """ persist plugin configuration and create default handlers. """ + + def __init__(self): + self.hide = [] + self.plugname = calledfrom(sys._getframe()) + Config.__init__(self, datadir + os.sep + 'plugs' + os.sep + self.plugname, "config") + + if self.plugname not in config['loadlist']: + return + + cmndname = "%s-cfg" % self.plugname + rlog(-3, 'persistconfig', 'added command %s (%s)' % (cmndname, \ +self.plugname)) + cmnds[cmndname] = Command(self.cmnd_cfg, 'OPER', self.plugname, \ +threaded=True) + examples.add(cmndname, "plugin configuration", cmndname) + cmndnamesave = cmndname + "save" + cmnds[cmndnamesave] = Command(self.cmnd_cfgsave, 'OPER', \ +self.plugname, threaded=True) + examples.add(cmndnamesave, "save plugin configuration", cmndnamesave) + + ### cmnds + + def show_cfg(self, bot, ievent): + + """ show config options. """ + + s = [] + + for key, optionvalue in sorted(self.iteritems()): + if key in self.hide: + continue + v = optionvalue + if type(v) in [str, unicode]: + v = '"'+v+'"' + v = str(v) + s.append("%s=%s" % (key, v)) + + ievent.reply("options: " + ' .. '.join(s)) + + def cmnd_cfgsave(self, bot, ievent): + + """ save config. """ + + self.save() + ievent.reply("config saved") + + def cmnd_cfg_edit(self, bot, ievent, args, key, optionvalue): + + """ edit config values. """ + + if not self.has_key(key): + ievent.reply('option %s is not defined' % key) + return + + if key in self.hide: + return + + if type(optionvalue) == types.ListType: + + if args[0].startswith("[") and args[-1].endswith("]"): + values = [] + + for v in ' '.join(args)[1:-1].replace(", ", ",").split(","): + if v[0]=='"' and v[-1]=='"': + # string + v = v.replace('"', '') + elif v[0]=="'" and v[-1]=="'": + # string + v = v.replace("'", "") + elif '.' in v: + # float + try: + v = float(v) + except ValueError: + ievent.reply("invalid long literal: %s" % v) + return + else: + # int + try: + v = int(v) + except ValueError: + ievent.reply("invalid int literal: %s" % v) + return + values.append(v) + + self.set(key, values) + self.save() + ievent.reply("%s set %s" % (key, values)) + return + + command = args[0] + value = ' '.join(args[1:]) + + if command == "clear": + self.clear(key) + self.save() + ievent.reply("list empty") + elif command == "add": + self.append(key, value) + self.save() + ievent.reply("%s added %s" % (key, value)) + elif command == "remove" or command == "del": + try: + self.remove(key, value) + self.save() + ievent.reply("%s removed" % str(value)) + except ValueError: + ievent.reply("%s is not in list" % str(value)) + else: + ievent.reply("invalid command") + return + + else: + value = ' '.join(args) + + try: + value = type(optionvalue)(value) + except: + pass + + if type(value) == type(optionvalue): + self.set(key, value) + self.save() + ievent.reply("%s set" % key) + elif type(value) == types.LongType and \ +type(option.value) == types.IntType: + # allow upscaling from int to long + self.set(key, value) + self.save() + ievent.reply("%s set" % key) + else: + ievent.reply("value %s (%s) is not of the same type as %s \ +(%s)" % (value, type(value), optionvalue, type(optionvalue))) + + def cmnd_cfg(self, bot, ievent): + + """ the config (cfg) command. """ + + if not ievent.args: + self.show_cfg(bot, ievent) + return + + argc = len(ievent.args) + key = ievent.args[0] + + try: + optionvalue = self[key] + except KeyError: + ievent.reply("%s option %s not found" % (self.plugname, key)) + return + + if key in self.hide: + return + + if argc == 1: + ievent.reply(str(optionvalue)) + return + + self.cmnd_cfg_edit(bot, ievent, ievent.args[1:], key, optionvalue) + + def generic_cmnd(self, key): + + """ command for editing config values. """ + + def func(bot, ievent): + + try: + optionvalue = self[key] + except KeyError: + ievent.reply("%s not found" % key) + # need return ? + + if not isinstance(option, Option): + rlog(5, 'persistconfig', 'Option %s is not a valid option' % \ +key) + return + + if ievent.args: + value = ' '.join(ievent.args) + try: + value = type(optionvalue)(value) + except: + pass + self.cmnd_cfg_edit(bot, ievent, ievent.args, key, optionvalue) + else: + ievent.reply(str(optionvalue)) + + return func + + ### plugin api + + def define(self, key, value=None, desc="plugin option", perm='OPER', \ +example="", name=None, exposed=True): + + """ define initial value. """ + + if name: + name = name.lower() + + if not exposed: + self.hide.append(key) + + if not self.has_key(key): + if name == None: + name = "%s-cfg-%s" % (self.plugname, str(key)) + self[key] = value + + def undefine(self, key, throw=False): + + """ remove a key. """ + + try: + del self[key] + return True + except KeyError, e: + if throw: + raise + + self.save() + return False + + def set(self, key, value, throw=False): + + """ set a key's value. """ + + self[key] = value + + def append(self, key, value): + + """ append a value. """ + + self[key].append(value) + + def remove(self, key, value): + + """ remove a value. """ + + self[key].remove(value) + + def clear(self, key): + + """ clear a value. """ + + self[key] = [] + + def get(self, key, default=None): + + """ get value of key. """ + + try: + return self[key] + except KeyError: + return default --- gozerbot-0.99.1.orig/build/lib/gozerbot/persist/persist.py +++ gozerbot-0.99.1/build/lib/gozerbot/persist/persist.py @@ -0,0 +1,156 @@ +# gozerbot/persist.py +# +# + +""" allow data to be written to disk in JSON format. creating the persisted + object restores data. +""" + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.stats import stats +from gozerbot.utils.locking import lockdec +from gozerbot.utils.log import rlog +from gozerbot.utils.trace import calledfrom +from gozerbot.utils.lazydict import LazyDict +from gozerbot.datadir import datadir +from gozerbot.config import config +from simplejson import load, dump, loads, dumps + +# basic imports +import pickle, thread, os, copy, sys, types + +saving = [] +stopsave = 0 + +persistlock = thread.allocate_lock() +persistlocked = lockdec(persistlock) + +if True: + + class Persist(object): + + """ persist data attribute to JSON file. """ + + def __init__(self, filename, default=None, init=True): + + """ Persist constructor """ + + self.fn = filename # filename to save to + self.lock = thread.allocate_lock() # lock used when saving) + self.data = LazyDict() # attribute to hold the data + + if init: + + if default == None: + default = LazyDict() + + self.init(default) + + @persistlocked + def init(self, default={}): + + """ initialize the data. """ + + rlog(0, 'persist', 'reading %s' % self.fn) + + # see if file exists .. if not initialize data to default + try: + datafile = open(self.fn, 'r') + + except IOError, ex: + + if not 'No such file' in str(ex): + rlog(10, 'persist', 'failed to read %s: %s' % (self.fn, str(ex))) + + self.data = copy.deepcopy(default) + raise + else: + return + + # load the JSON data into attribute + try: + self.data = load(datafile) + datafile.close() + stats.up('persist', 'load') + + if type(self.data) == types.DictType: + + d = LazyDict() + d.update(self.data) + self.data = d + + except Exception, ex: + rlog(100, 'persist', 'ERROR: %s' % self.fn) + raise + + @persistlocked + def save(self): + + """ persist data attribute. """ + + if stopsave: + rlog(100, 'persist', 'stopping mode .. not saving %s' % self.fn) + return + + # save data + try: + #self.lock.acquire() + saving.append(str(self.fn)) + tmp = self.fn + '.tmp' # tmp file to save to + + # first save to temp file and when done rename + try: + datafile = open(tmp, 'w') + + except IOError, ex: + rlog(100, 'persist', "can't save %s: %s" % (self.fn, str(ex))) + return + + # dump JSON to file + #cp = copy.copy(self.data) + dump(self.data, datafile) + datafile.close() + + try: + os.rename(tmp, self.fn) + + except WindowsError: + # no atomic operation supported on windows! error is thrown when destination exists + os.remove(self.fn) + os.rename(tmp, self.fn) + + stats.up('persist', 'saved') + rlog(10, 'persist', '%s saved' % self.fn) + + finally: + saving.remove(self.fn) + #self.lock.release() + + +class PlugPersist(Persist): + + """ persist plug related data. """ + + def __init__(self, filename, default=None): + + # retrieve plugname where object is constructed + plugname = calledfrom(sys._getframe()) + + # call base constructor with appropiate filename + Persist.__init__(self, datadir + os.sep + 'plugs' + os.sep + plugname + os.sep + filename, default) + + +class LazyDictPersist(Persist): + + """ persisted lazy dict. """ + + def __init__(self, default={}): + + # called parent constructor + Persist.__init__(self) + + # if data not initialised set it to a LazyDict + if not self.data: + self.data = LazyDict(default) --- gozerbot-0.99.1.orig/build/lib/gozerbot/persist/persiststate.py +++ gozerbot-0.99.1/build/lib/gozerbot/persist/persiststate.py @@ -0,0 +1,68 @@ +# gozerbot/persiststate.py +# +# + +""" persistent state classes """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.trace import calledfrom +from gozerbot.utils.log import rlog +from gozerbot.datadir import datadir +from persist import Persist +import types, os, sys + +class PersistState(Persist): + + """ base persitent state class """ + + def __init__(self, filename): + Persist.__init__(self, filename, {}) + self.types = dict((i, type(j)) for i, j in self.data.iteritems()) + + def __getitem__(self, key): + + """ get state item. """ + + return self.data[key] + + def __setitem__(self, key, value): + + """ set state item. """ + + self.data[key] = value + + def define(self, key, value): + + """ define a state item. """ + + if not self.data.has_key(key) or type(value) != self.types[key]: + if type(value) == types.StringType: + value = unicode(value) + if type(value) == types.IntType: + value = long(value) + self.data[key] = value + +class PlugState(PersistState): + + """ state for plugins. """ + + def __init__(self): + self.plugname = calledfrom(sys._getframe()) + rlog(1, 'persiststate', 'initialising %s' % self.plugname) + PersistState.__init__(self, datadir + os.sep + 'plugs' + os.sep + self.plugname + os.sep + 'state') + +class ObjectState(PersistState): + + """ state for usage in constructors. """ + + def __init__(self): + PersistState.__init__(self, datadir + os.sep + calledfrom(sys._getframe(1))+'.state') + +class UserState(PersistState): + + """ state for users. """ + + def __init__(self, username): + self.datadir = datadir + os.sep + 'users' + os.sep + username + PersistState.__init__(self, self.datadir + os.sep + 'state') --- gozerbot-0.99.1.orig/build/lib/gozerbot/persist/pdod.py +++ gozerbot-0.99.1/build/lib/gozerbot/persist/pdod.py @@ -0,0 +1,75 @@ +# gozerbot/pdod.py +# +# + +""" pickled dicts of dicts """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.locking import lockdec +from persist import Persist +import thread + +pdodlock = thread.allocate_lock() +locked = lockdec(pdodlock) + +class Pdod(Persist): + + """ pickled dicts of dicts """ + + def __init__(self, filename): + Persist.__init__(self, filename) + if not self.data: + self.data = {} + + def __getitem__(self, name): + """ return item with name """ + if self.data.has_key(name): + return self.data[name] + + #@locked + def save(self): + Persist.save(self) + + #@locked + def __delitem__(self, name): + """ delete name item """ + if self.data.has_key(name): + return self.data.__delitem__(name) + + #@locked + def __setitem__(self, name, item): + """ set name item """ + self.data[name] = item + + def __contains__(self, name): + return self.data.__contains__(name) + + #@locked + def setdefault(self, name, default): + """ set default of name """ + return self.data.setdefault(name, default) + + def has_key(self, name): + """ has name key """ + return self.data.has_key(name) + + def has_key2(self, name1, name2): + """ has [name1][name2] key """ + if self.data.has_key(name1): + return self.data[name1].has_key(name2) + + def get(self, name1, name2): + """ get data[name1][name2] """ + try: + result = self.data[name1][name2] + return result + except KeyError: + return None + + #@locked + def set(self, name1, name2, item): + """ set name, name2 item """ + if not self.data.has_key(name1): + self.data[name1] = {} + self.data[name1][name2] = item --- gozerbot-0.99.1.orig/build/lib/gozerbot/persist/pdol.py +++ gozerbot-0.99.1/build/lib/gozerbot/persist/pdol.py @@ -0,0 +1,72 @@ +# gozerbot/pdol.py +# +# + +""" pickled dict of lists """ + +__copyright__ = 'this file is in the public domain' + +from persist import Persist + +class Pdol(Persist): + + """ pickled dict of lists """ + + def __init__(self, fname): + Persist.__init__(self, fname) + if not self.data: + self.data = {} + + def __iter__(self, name): + return self.data[name].__iter__() + + def __getitem__(self, item): + if self.data.has_key(item): + return self.data[item] + + def __delitem__(self, item): + if self.data.has_key(item): + self.data.__delitem__(item) + return 1 + + def __setitem__(self, item, what): + if self.data.has_key(item): + self.data[item].append(what) + else: + self.data[item] = [what] + return 1 + + def add(self, item, what): + """ add what to items list """ + return self.__setitem__(item, what) + + def adduniq(self, item, what): + """ add what to items list if item not yet added """ + if not self.data.has_key(item): + self.new(item) + if what not in self.data[item]: + return self.__setitem__(item, what) + + def get(self, item): + """ get items list """ + return self.__getitem__(item) + + def new(self, what): + """ reset list of what """ + self.data[what] = [] + + def delete(self, item, what): + """ remove what from item's list """ + del self.data[item][what] + + def extend(self, item, what): + if not self.data.has_key(item): + self.new(item) + self.data[item].extend(what) + + def remove(self, item, what): + try: + self.data[item].remove(what) + return 1 + except (ValueError, KeyError): + return 0 --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/textutils.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/textutils.py @@ -0,0 +1,34 @@ +import cgi +import re +import htmlentitydefs + +def unescape_charref(ref): + name = ref[2:-1] + base = 10 + if name.startswith("x"): + name = name[1:] + base = 16 + return unichr(int(name, base)) +def replace_entities(match): + ent = match.group() + if ent[1] == "#": + return unescape_charref(ent) + repl = htmlentitydefs.name2codepoint.get(ent[1:-1]) + if repl is not None: + repl = unichr(repl) + else: + repl = ent + return repl + +def html_unescape(data): + ''' + Unescape (numeric) HTML entities. + ''' + return re.sub(r"&#?[A-Za-z0-9]+?;", replace_entities, data) + +def html_escape(data): + ''' + Escape HTML entities. + ''' + return cgi.escape(data) + --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/url.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/url.py @@ -0,0 +1,259 @@ +# gozerbot/utils/url.py +# +# + +""" generic functions """ + +__copyright__ = 'this file is in the public domain' + +from log import rlog +from generic import fromenc +from gozerbot.datadir import datadir +from gozerbot.config import config +import time, sys, re, traceback, Queue, urllib, urllib2, urlparse, socket +import random, os, sgmllib, thread, types, httplib, StringIO +import htmlentitydefs, tempfile, cgi + +try: + import chardet +except ImportError: + chardet = None + +class istr(str): + pass + +def useragent(): + """ provide useragent string """ + (name, version) = config['version'].split()[0:2] + return 'Mozilla/5.0 (compatible; %s %s; http://gozerbot.googlecode.com)' % \ +(name, version) + +class CBURLopener(urllib.FancyURLopener): + + """ our URLOpener """ + + def __init__(self, version, *args): + if version: + self.version = version + else: + self.version = useragent() + urllib.FancyURLopener.__init__(self, *args) + +def geturl(url, version=None): + """ fetch an url """ + urllib._urlopener = CBURLopener(version) + rlog(10, 'url', 'fetching %s' % url) + result = urllib.urlopen(url) + tmp = result.read() + result.close() + return tmp + +def geturl2(url, decode=False): + """ use urllib2 to fetch an url """ + rlog(10, 'url', 'fetching %s' % url) + request = urllib2.Request(url) + request.add_header('User-Agent', useragent()) + opener = urllib2.build_opener() + result = opener.open(request) + tmp = result.read() + info = result.info() # add header information to .info attribute + result.close() + if decode: + encoding = get_encoding(tmp) + rlog(0, 'url', '%s encoding: %s' % (url, encoding)) + res = istr(fromenc(tmp, encoding, url)) + else: + res = istr(tmp) + res.info = info + return res + +def geturl3(url, myheaders={}, postdata={},keyfile='', certfile="", port=80): + return geturl2(url) + +def geturl4(url, myheaders={}, postdata={}, keyfile="", certfile="", port=80): + headers = {'Content-Type': 'text/html', \ +'Accept': 'text/plain; text/html', 'User-Agent': useragent()} + headers.update(myheaders) + # parse URL components + urlparts = urlparse.urlparse(url) + try: + port = int(urlparts[1].split(':')[1]) + host = urlparts[1].split(':')[0] + except: + host = urlparts[1] + # set up HTTP connection + if keyfile: + connection = httplib.HTTPSConnection(host, port, keyfile, \ +certfile) + elif 'https' in urlparts[0]: + connection = httplib.HTTPSConnection(host, port) + else: + connection = httplib.HTTPConnection(host, port) + # make the request + if type(postdata) == types.DictType: + postdata = urllib.urlencode(postdata) + rlog(10, 'url', 'fetching %s' % url) + connection.request('GET', urlparts[2]) + # read the response and clean up + return connection.getresponse() + + +def posturl(url, myheaders, postdata, keyfile=None, certfile="",port=80): + """ very basic HTTP POST url retriever """ + # build headers + headers = {'Content-Type': 'application/x-www-form-urlencoded', \ +'Accept': 'text/plain; text/html', 'User-Agent': useragent()} + headers.update(myheaders) + # parse URL components + urlparts = urlparse.urlparse(url) + # set up HTTP connection + if keyfile: + connection = httplib.HTTPSConnection(urlparts[1], port, keyfile, \ +certfile) + else: + connection = httplib.HTTPConnection(urlparts[1]) + # make the request + if type(postdata) == types.DictType: + postdata = urllib.urlencode(postdata) + rlog(10, 'url', 'fetching %s' % url) + connection.request('POST', urlparts[2], postdata, headers) + # read the response and clean up + return connection.getresponse() + +def deleteurl(url, myheaders={}, postdata={}, keyfile="", certfile="", port=80): + """ very basic HTTP POST url retriever """ + # build headers + headers = {'Content-Type': 'application/x-www-form-urlencoded', \ +'Accept': 'text/plain; text/html', 'User-Agent': useragent()} + headers.update(myheaders) + # parse URL components + urlparts = urlparse.urlparse(url) + # set up HTTP connection + if keyfile and certfile: + connection = httplib.HTTPSConnection(urlparts[1], port, keyfile, \ +certfile) + else: + connection = httplib.HTTPConnection(urlparts[1]) + # make the request + if type(postdata) == types.DictType: + postdata = urllib.urlencode(postdata) + rlog(10, 'url', 'fetching %s' % url) + connection.request('DELETE', urlparts[2], postdata, headers) + # read the response and clean up + return connection.getresponse() + +def puturl(url, myheaders={}, postdata={}, keyfile="", certfile="", port=80): + """ very basic HTTP POST url retriever """ + # build headers + headers = {'Content-Type': 'application/x-www-form-urlencoded', \ +'Accept': 'text/plain; text/html', 'User-Agent': useragent()} + headers.update(myheaders) + # parse URL components + urlparts = urlparse.urlparse(url) + # set up HTTP connection + if keyfile: + connection = httplib.HTTPSConnection(urlparts[1], port, keyfile, \ +certfile) + else: + connection = httplib.HTTPConnection(urlparts[1]) + # make the request + if type(postdata) == types.DictType: + postdata = urllib.urlencode(postdata) + rlog(10, 'url', 'fetching %s' % url) + connection.request('PUT', urlparts[2], postdata, headers) + # read the response and clean up + return connection.getresponse() + +def getpostdata(event): + ctype, pdict = cgi.parse_header(event.headers.getheader('content-type')) + body = cgi.FieldStorage(fp=event.rfile, headers=event.headers, \ +environ = {'REQUEST_METHOD':'POST'}, keep_blank_values = 1) + result = {} + for name in dict(body): + result[name] = body.getfirst(name) + return result + +def decode_html_entities(s): + """ smart decoding of html entities to utf-8 """ + re_ent_match = re.compile(u'&([^;]+);') + re_entn_match = re.compile(u'&#([^x;]+);') + re_entnx_match = re.compile(u'&#x([^;]+);') + s = s.decode('utf-8', 'replace') + def to_entn(match): + """ convert to entities """ + if htmlentitydefs.entitydefs.has_key(match.group(1)): + return htmlentitydefs.entitydefs[match.group(1)].decode('utf-8', \ +'replace') + return match.group(0) + def to_utf8(match): + """ convert to utf-8 """ + return unichr(long(match.group(1))) + def to_utf8x(match): + """ convert to utf-8 """ + return unichr(long(match.group(1), 16)) + s = re_ent_match.sub(to_entn, s) + s = re_entn_match.sub(to_utf8, s) + s = re_entnx_match.sub(to_utf8x, s) + return s + +def get_encoding(data): + """ get encoding from web data """ + # first we try if we have the .info attribute to determine the encoding from + if hasattr(data, 'info') and data.info.has_key('content-type') and \ +'charset' in data.info['content-type'].lower(): + charset = data.info['content-type'].lower().split('charset', 1)[1].\ +strip() + if charset[0] == '=': + charset = charset[1:].strip() + if ';' in charset: + return charset.split(';')[0].strip() + return charset + # try to find the charset in the meta tags, + # + if ']+>', data, re.I | re.M) + if metas: + for meta in metas: + test_http_equiv = \ +re.search('http-equiv\s*=\s*[\'"]([^\'"]+)[\'"]', meta, re.I) + if test_http_equiv and \ +test_http_equiv.group(1).lower() == 'content-type': + test_content = \ +re.search('content\s*=\s*[\'"]([^\'"]+)[\'"]', meta, re.I) + if test_content: + test_charset = \ +re.search('charset\s*=\s*([^\s\'"]+)', meta, re.I) + if test_charset: + return test_charset.group(1) + # everything else failed, let's see if we can use chardet + if chardet: + test = chardet.detect(data) + if test.has_key('encoding'): + return test['encoding'] + # nothing found, let's fall back to the default encoding + return sys.getdefaultencoding() + +class Stripper(sgmllib.SGMLParser): + + """ html stripper """ + + def __init__(self): + sgmllib.SGMLParser.__init__(self) + + def strip(self, some_html): + """ strip html """ + self.theString = "" + self.feed(some_html) + self.close() + return self.theString + + def handle_data(self, data): + """ data handler """ + self.theString += fromenc(data) + +def striphtml(txt): + """ strip html from txt """ + stripper = Stripper() + txt = stripper.strip(fromenc(txt)) + return txt + --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/lockmanager.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/lockmanager.py @@ -0,0 +1,56 @@ +# gozerbot/lockmanager.py +# +# + +""" manages locks """ + +__copyright__ = 'this file is in the public domain' + +from log import rlog +import thread, threading + +class Lockmanager(object): + + """ place to hold locks """ + + def __init__(self): + self.locks = {} + + def allocate(self, name): + """ allocate a new lock """ + self.locks[name] = thread.allocate_lock() + rlog(0, 'lockmanager', 'allocated %s' % name) + + def get(self, name): + """ get lock """ + if not self.locks.has_key(name): + self.allocate(name) + return self.locks[name] + + def delete(self, name): + """ delete lock """ + if self.locks.has_key(name): + del self.locks[name] + + def acquire(self, name): + """ acquire lock """ + if not self.locks.has_key(name): + self.allocate(name) + rlog(0, 'lockmanager', 'acquire %s' % name) + self.locks[name].acquire() + + def release(self, name): + """ release lock """ + rlog(0, 'lockmanager', 'releasing %s' % name) + self.locks[name].release() + + +class RLockManager(Lockmanager): + + def allocate(self, name): + """ allocate a new lock """ + self.locks[name] = threading.RLock() + rlog(0, 'lockmanager', 'allocated RLock %s' % name) + +lockmanager = Lockmanager() +rlockmanager = RLockManager() --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/statdict.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/statdict.py @@ -0,0 +1,45 @@ +# gozerbot/statdict.py +# +# + +""" stats dict """ + +__copyright__ = 'this file is in the public domain' + +class Statdict(dict): + + """ dictionary to hold stats """ + + def set(self, item, value): + """ set item to value """ + self[item] = value + + def upitem(self, item, value=1): + """ increase item """ + if not self.has_key(item): + self[item] = value + return + self[item] += value + + def top(self, start=1, limit=None): + """ return highest items """ + result = [] + for item, value in self.iteritems(): + if value >= start: + result.append((item, value)) + result.sort(lambda b, a: cmp(a[1], b[1])) + if limit: + result = result[:limit] + return result + + def down(self, end=100, limit=None): + """ return lowest items """ + result = [] + for item, value in self.iteritems(): + if value <= end: + result.append((item, value)) + result.sort(lambda a, b: cmp(a[1], b[1])) + if limit: + return result[:limit] + else: + return result --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/generic.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/generic.py @@ -0,0 +1,513 @@ +# gozerbot/utils/generic.py +# +# + +""" generic functions """ + +__copyright__ = 'this file is in the public domain' + + +from gozerbot.persist.persist import Persist +from exception import handle_exception +from trace import calledfrom +from stat import ST_UID, ST_MODE, S_IMODE +from gozerbot.config import config +from log import rlog +from lazydict import LazyDict +from simplejson import dumps +import time, sys, re, getopt, types, os, random, socket, Queue + +def loadeggs(eggdir, version=None): + rlog(10, 'booting', 'scanning %s' % eggdir) + for f in os.listdir(eggdir): + if f.endswith('.egg'): + rlog(10, 'booting', 'loading %s' % f) + try: + from pkg_resources import require + require("%s>0.0") + except ImportEror: + sys.path.insert(0, eggdir + os.sep + f) + +def jsonstring(s): + if type(s) == types.TupleType: + s = list(s) + return dumps(s) + +def stripident(userhost): + """ strip ident char from userhost """ + try: + userhost.getNode() + return str(userhost) + except AttributeError: + pass + if not userhost: + return None + if userhost[0] in "~-+^": + userhost = userhost[1:] + elif userhost[1] == '=': + userhost = userhost[2:] + return userhost + +def stripidents(ulist): + """ strip ident char from list of userhosts """ + result = [] + for userhost in ulist: + result.append(stripident(userhost)) + return result + +def makedirs(datadir): + if not os.path.isdir(datadir): + os.mkdir(datadir) + if not os.path.isdir(datadir + '/states/'): + os.mkdir(datadir + '/states/') + if not os.path.isdir(datadir + '/db/'): + os.mkdir(datadir + '/db/') + if not os.path.isdir(datadir + '/configs/'): + os.mkdir(datadir + '/configs/') + +def cleanpyc(): + removed = [] + try: + files = os.listdir('gplugs') + for file in files: + if file.endswith('.pyc'): + os.unlink('gplugs' + os.sep + file) + removed.append(file) + except: + pass + try: + files = os.listdir('gplugs/olddb') + for file in files: + if file.endswith('.pyc'): + os.unlink('gplugs/olddb' + os.sep + file) + removed.append(file) + except: + pass + try: + files = os.listdir('gplugs/alchemy') + for file in files: + if file.endswith('.pyc'): + os.unlink('gplugs/alchemy' + os.sep + file) + removed.append(file) + except: + pass + return removed + +def cleanpycfile(filename): + try: + if filename.endswith('.pyc'): + os.unlink(filename) + rlog(10, 'generic', 'cleaned %s' % filename) + except: + pass + +def getversion(): + version = config['version'] + if config['nodb']: + version += ' JSON_USERS' + else: + driver = config.get("db_driver") + if driver: driver = driver.upper() + version += ' ' + config['dbtype'].upper() + ' ' + driver or "" + return version + +def makeoptions(ievent, options={}): + options = LazyDict(options) + try: + optargs = "" + optlist = [] + if not options.has_key('--filter'): + options['--filter'] = "" + if not options.has_key('--to'): + options['--to'] = None + if not options.has_key('--chan'): + options['--chan'] = ievent.channel + if not options.has_key('--how'): + options['--how'] = "msg" + if not options.has_key('--speed'): + options['--speed'] = str(ievent.speed) + else: + options['--speed'] = str(options['--speed']) + for i, j in options.iteritems(): + if '--' in i: + optlist.append("%s=" % i[2:]) + if j: + optlist.append(j) + continue + if '-' in i: + if j: + optargs += ":%s" % i[1:] + else: + optargs += i[1:] + args = ievent.txt.split() + try: + (opts, rest) = getopt.getopt(args[1:], optargs, optlist) + except AttributeError, ex: + print "option not allowed: %s" % str(ex), ievent.txt, options + return 0 + except getopt.GetoptError, ex: + return 0 + if opts: + for item in opts: + ievent.optionset.append(item[0]) + o = dict(options) + o.update(dict(opts)) + try: + filter = o['--filter'] + if filter and filter not in ievent.filter: + ievent.filter.append(filter) + except KeyError: + pass + try: + speed = o['--speed'] + ievent.speed = int(speed) + except KeyError: + pass + try: + ievent.channel = o['--chan'] or ievent.channel + except KeyError: + pass + ievent.options.update(o) + if args: + ievent.txt = args[0] + ' ' + ' '.join(rest) + makeargrest(ievent) + except Exception, ex: + handle_exception() + return + return ievent.options + +def makeargrest(ievent): + """ create ievent.args and ievent.rest .. this is needed because \ + ircevents might be created outside the parse() function """ + try: + ievent.args = ievent.txt.split()[1:] + except ValueError: + ievent.args = [] + try: + cmnd, ievent.rest = ievent.txt.split(' ', 1) + except ValueError: + ievent.rest = "" + ievent.command = ievent.txt.split(' ')[0] + +def setdefenc(encoding): + import sys + reload(sys) + sys.setdefaultencoding(encoding) + +def plugfile(datadir): + return datadir + os.sep + calledfrom(sys._getframe()) + +def cchar(bot, ievent): + try: + cchar = bot.channels[ievent.channel]['cc'] + except LookupError: + cchar = config['defaultcc'] or '!' + except TypeError: + cchar = config['defaultcc'] or '!' + return cchar + +def splittxt(what, l=375): + txtlist = [] + start = 0 + end = l + length = len(what) + for i in range(length/end+1): + endword = what.find(' ', end) + if endword == -1: + endword = length + res = what[start:endword] + if res: + txtlist.append(res) + start = endword + end = start + l + return txtlist + +class istr(str): + pass + +def die(): + os.kill(os.getpid(), 9) + +def getlistensocket(listenip): + port = 5000 + while 1: + time.sleep(0.01) + try: + port += 1 + if ':' in listenip: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + else: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.setblocking(1) + if port > 65000: + port = 5000 + sock.bind((listenip, port)) + sock.listen(1) + return (port, sock) + except Exception, ex: + pass + +def checkchan(bot, item): + chanre = re.search(' chan (\S+)', item) + if chanre: + chan = str(chanre.group(1)) + item = re.sub(' chan ' + re.escape(chan), '', item) + return (chan.lower(), item) + +def getwho(bot, who): + """ get userhost from bots userhost cache """ + try: + result = bot.userhosts.data[who] + if bot.cfg['stripident']: + rlog(10, 'getwho', 'removed ident from %s' % result) + result = stripident(result) + return result + except KeyError: + return None + +def waitforuser(bot, userhost, timeout=15): + queue = Queue.Queue() + waitnr = bot.privwait.register(userhost, queue, timeout) + result = queue.get() + bot.privwait.delete(waitnr) + return result + +def getrandomnick(): + return "gbot" + str(random.randint(0, 100)) + +def waitforqueue(queue, timeout=10, maxitems=None): + result = [] + while 1: + try: + res = queue.get(1, timeout) + except Queue.Empty: + break + if not res: + break + result.append(res) + if maxitems and len(result) == maxitems: + break + return result + +def decodeperchar(txt, encoding='utf-8', what=""): + + res = [] + nogo = [] + + for i in txt: + try: + res.append(i.decode(encoding)) + except UnicodeDecodeError: + if i not in nogo: + nogo.append(i) + continue + + if nogo: + if what: + rlog(10, 'generic', "%s: can't decode %s characters to %s" % (what, nogo, encoding)) + else: + rlog(10, 'generic', "can't decode %s characters to %s" % (nogo, encoding)) + + return u"".join(res) + +def toenc(what, encoding='utf-8'): + if not what: + return u"" + try: + w = unicode(what) + return w.encode(encoding) + except UnicodeEncodeError: + rlog(10, 'generic', "can't encode %s to %s" % (what, encoding)) + return u"" + +def fromenc(txt, encoding='utf-8', what=""): + if not txt: + return u"" + try: + if type(txt) == types.UnicodeType: + t = txt.encode(encoding) + t = unicode(txt) + return unicode(t.decode(encoding)) + except UnicodeDecodeError: + return decodeperchar(txt, encoding, what) + +def toascii(what): + what = what.encode('ascii', 'replace') + return what + +def tolatin1(what): + what = what.encode('latin-1', 'replace') + return what + +def strippedtxt(what, allowed=[]): + txt = [] + allowed = allowed + ['\001', '\002', '\003', '\t'] + for i in what: + if ord(i) > 31 or (allowed and i in allowed): + txt.append(i) + return ''.join(txt) + +def uniqlist(l): + result = [] + for i in l: + j = i.strip() + if j not in result: + result.append(j) + return result + +def fix_format(s): + counters = { + chr(2): 0, + chr(3): 0 + } + for letter in s: + if letter in counters: + counters[letter] += 1 + for char in counters: + if counters[char] % 2: + s += char + return s + +def stripbold(s): + s = s.replace(chr(2), '') + s = s.replace(chr(3), '') + return s + +def jabberstrip(text, allowed=[]): + txt = [] + allowed = allowed + ['\n', '\t'] + for i in text: + if ord(i) > 31 or (allowed and i in allowed): + txt.append(i) + return ''.join(txt) + +def plugnames(dirname): + result = [] + for i in os.listdir(dirname): + if os.path.isdir(dirname + os.sep + i): + if os.path.exists(dirname + os.sep + i + os.sep + '__init__.py'): + result.append(i) + elif i.endswith('.py'): + result.append(i[:-3]) + try: + result.remove('__init__') + except: + pass + return result + +def filesize(path): + return os.stat(path)[6] + +def touch(fname): + fd = os.open(fname, os.O_WRONLY | os.O_CREAT) + os.close(fd) + +def stringinlist(s, l): + for i in l: + if s in i: + return 1 + +def stripped(userhost): + return userhost.split('/')[0] + +def checkpermissions(ddir, umode): + try: + uid = os.getuid() + gid = os.getgid() + except AttributeError: + return + try: + stat = os.stat(ddir) + except OSError: + return + if stat[ST_UID] != uid: + try: + os.chown(ddir, uid, gid) + except: + pass + if S_IMODE(stat[ST_MODE]) != umode: + try: + os.chmod(ddir, umode) + except: + handle_exception() + pass + +def gethighest(ddir, ffile): + highest = 0 + for i in os.listdir(ddir): + if os.path.isdir(ddir + os.sep + i) and ffile in i: + try: + seqnr = i.split('.')[2] + except IndexError: + continue + try: + if int(seqnr) > highest: + highest = int(seqnr) + except ValueError: + pass + ffile += '.' + str(highest + 1) + return ffile + +def copyfile(src, target): + try: + pdir = os.sep.join(target.split(os.sep)[:-1]) + if not pdir: + print "can't determine parent dir of %s" % src + return + if not os.path.isdir(pdir): + print "making %s dir" % pdir + os.mkdir(pdir) + print "copying %s to %s" % (src, target) + shutil.copy(src, target) + dosed(target, 's/gozerbot\.compat\./gozerplugs\./') + except IOError, ex: + return + #print "%s -> %s: %s" % (src, target, str(ex)) + except Exception, ex: + handle_exception() + +def dosed(filename, sedstring): + try: + f = open(filename, 'r') + except IOError, ex: + if 'Is a dir' in str(ex): + return + else: + raise + tmp = filename + '.tmp' + fout = open(tmp, 'w') + seds = sedstring.split('/') + fr = seds[1].replace('\\', '') + to = seds[2].replace('\\', '') + try: + for line in f: + l = line.replace(fr,to) + fout.write(l) + finally: + fout.flush() + fout.close() + try: + os.rename(tmp, filename) + except WindowsError: + # no atomic operation supported on windows! error is thrown when destination exists + os.remove(filename) + os.rename(tmp, filename) + +def convertpickle(src, target): + import gozerbot.compat.persist + p = gozerbot.compat.persist.Persist(src) + if p and p.data: + pers = Persist(target) + if not pers.data: + pers.data = {} + pers.data.update(p.data) + try: + pers.save() + except TypeError: + pers2 = Persist(target) + if not pers2.data: + pers2.data = {} + for item, value in p.data.iteritems(): + pers2.data[jsonstring(item)] = value + pers2.save() --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/exception.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/exception.py @@ -0,0 +1,89 @@ +# gozerbot/utils/exception.py +# +# + +""" generic functions """ + +__copyright__ = 'this file is in the public domain' + +# ============== +# IMPORT SECTION + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.utils.locking import lockdec + +# basic imports +import sys, traceback, logging, thread, os + +# END IMPORT +# ========== + +# ============ +# LOCK SECTION + +exceptionlock = thread.allocate_lock() +exceptionlocked = lockdec(exceptionlock) + +borkonexception = False + +# END LOCK +# ======== + +@exceptionlocked +def exceptionmsg(): + exctype, excvalue, tb = sys.exc_info() + trace = traceback.extract_tb(tb) + result = "" + for i in trace: + fname = i[0] + linenr = i[1] + func = i[2] + result += "%s:%s %s | " % (fname, linenr, func) + del trace + res = "%s%s: %s" % (result, exctype, excvalue) + if res not in exceptionlist: + exceptionlist.append(res) + if '_props' in res: + print + return res + +def handle_exception(event=None, log=True, short=False, txt=""): + global borkonexception + errormsg = exceptionmsg() + + if event: + if '_props' in errormsg: + print "YOOOOOOOOOOOO" + print event + os._exit(1) + + if log: + rlog(1000, 'EXCEPTION', errormsg) + + if event: + + if borkonexception: + rlog(1000, 'EXCEPTION', 'command: ' + event.txt) + rlog(1000, 'EXCEPTION', 'options: ' + str(event.options)) + + exceptionevents.append((event, errormsg)) + + if event.bot: + event.bot.error = errormsg + + event.reply(errormsg) + + if borkonexception: + rlog(1000, 'EXCEPTION', "BORK ON EXCEPTION") + sys.stdout.flush() + os._exit(1) + +# ============ +# INIT SECTION + +exceptionlist = [] +exceptionevents = [] + +# END INIT +# ======== --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/rsslist.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/rsslist.py @@ -0,0 +1,40 @@ +# gozerbot/rsslist.py +# +# + +""" create a list of rss data """ + +__copyright__ = 'this file is in the public domain' + +from exception import handle_exception +import xml.dom.minidom + +def gettext(nodelist): + """ get text data from nodelist """ + result = "" + for node in nodelist: + if node.nodeType == node.TEXT_NODE or node.nodeType == \ +node.CDATA_SECTION_NODE: + stripped = node.data.strip() + if stripped: + result += stripped + return result + +def makersslist(xlist, nodes , d={}): + """ recurse until txt is found """ + for i in nodes: + if i.nodeType == i.ELEMENT_NODE: + dd = d[i.nodeName] = {} + makersslist(xlist, i.childNodes, dd) + if dd: + xlist.append(dd) + txt = gettext(i.childNodes) + if txt: + d[i.nodeName] = txt + +def rsslist(txt): + """ create list of dictionaries with rss data """ + dom = xml.dom.minidom.parseString(txt) + result = [] + makersslist(result, dom.childNodes) + return result --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/timeutils.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/timeutils.py @@ -0,0 +1,230 @@ +# gozerbot/utils/timeutils.py +# +# + +""" time related helper functions """ + +__copyright__ = 'this file is in the public domain' + +from exception import handle_exception +import time, re, calendar + +leapfactor = float(6*60*60)/float(365*24*60*60) + +def elapsedstring(nsec, ywd = None): + nsec = int(float(nsec)) + year = 365*24*60*60 + week = 7*24*60*60 + day = 24*60*60 + hour = 60*60 + minute = 60 + nsec -= nsec * leapfactor + years = int(nsec/year) + nsec -= years*year + weeks = int(nsec/week) + nsec -= weeks*week + days = int(nsec/day) + nsec -= days*day + hours = int(nsec/hour) + nsec -= hours*hour + minutes = int(nsec/minute) + sec = int(nsec - minutes*minute) + result = '' + if (years > 1): + result = str(years) + " years " + if (years == 1): + result = "1 year " + if (weeks > 1): + result += str(weeks) + " weeks " + if (weeks == 1): + result += "1 week " + if (days > 1): + if ywd: + result += 'and '+ str(days) + " days" + else: + result += str(days) + " days " + if (days == 1): + if ywd: + result += 'and 1 day' + else: + result += "1 day " + if ywd: + return result + if (hours > 1): + result += str(hours) + " hours " + if (hours == 1): + result += "1 hour " + if (minutes > 1): + result += str(minutes) + " minutes " + if (minutes == 1): + result += "1 minute " + if sec == 0: + if result: + return result + else: + return 0 + if (sec == 1): + if result: + result += "and 1 second " + else: + result = "1 second" + else: + if result: + result += "and " + str(sec) + " seconds" + else: + result = str(sec) + " seconds" + return result.strip() + +def hourmin(ttime): + result = "" + timeres = time.localtime(ttime) + if timeres[3] < 10: + result += "0" + str(timeres[3]) + ":" + else: + result += str(timeres[3]) + ":" + if timeres[4] < 10: + result += "0" + str(timeres[4]) + else: + result += str(timeres[4]) + return result + +timere = re.compile('(\S+)\s+(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)') + +bdmonths = ['Bo', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', \ +'Sep', 'Oct', 'Nov', 'Dec'] + +def striptime(what): + """ strip time indicators from string """ + what = str(what) + what = re.sub('\d+-\d+-\d+', '', what) + what = re.sub('\d+-\d+', '', what) + what = re.sub('\d+:\d+', '', what) + what = re.sub('\s+', ' ', what) + return what.strip() + +def now(): + if time.daylight: + ttime = time.ctime(time.time() + int(time.timezone) + 3600) + else: + ttime = time.ctime(time.time() + int(time.timezone)) + return ttime + +def today(): + if time.daylight: + ttime = time.ctime(time.time() + int(time.timezone) + 3600) + else: + ttime = time.ctime(time.time() + int(time.timezone)) + matched = re.search(timere, ttime) + if matched: + temp = "%s %s %s" % (matched.group(3), matched.group(2), \ +matched.group(7)) + timestring = time.strptime(temp, "%d %b %Y") + result = time.mktime(timestring) + return result + +def strtotime(what): + daymonthyear = 0 + hoursmin = 0 + try: + dmyre = re.search('(\d+)-(\d+)-(\d+)', str(what)) + if dmyre: + (day, month, year) = dmyre.groups() + day = int(day) + month = int(month) + year = int(year) + if day <= calendar.monthrange(year, month)[1]: + date = "%s %s %s" % (day, bdmonths[month], year) + daymonthyear = time.mktime(time.strptime(date, "%d %b %Y")) + else: + return None + else: + dmre = re.search('(\d+)-(\d+)', str(what)) + if dmre: + year = time.localtime()[0] + (day, month) = dmre.groups() + day = int(day) + month = int(month) + if day <= calendar.monthrange(year, month)[1]: + date = "%s %s %s" % (day, bdmonths[month], year) + daymonthyear = time.mktime(time.strptime(date, "%d %b %Y")) + else: + return None + hmsre = re.search('(\d+):(\d+):(\d+)', str(what)) + if hmsre: + (h, m, s) = hmsre.groups() + h = int(h) + m = int(m) + s = int(s) + if h > 24 or h < 0 or m > 60 or m < 0 or s > 60 or s < 0: + return None + hours = 60 * 60 * (int(hmsre.group(1))) + hoursmin = hours + int(hmsre.group(2)) * 60 + hms = hoursmin + int(hmsre.group(3)) + else: + hmre = re.search('(\d+):(\d+)', str(what)) + if hmre: + (h, m) = hmre.groups() + h = int(h) + m = int(m) + if h > 24 or h < 0 or m > 60 or m < 0: + return None + hours = 60 * 60 * (int(hmre.group(1))) + hms = hours + int(hmre.group(2)) * 60 + else: + hms = 0 + if not daymonthyear and not hms: + return None + if daymonthyear == 0: + heute = today() + else: + heute = daymonthyear + return heute + hms + except OverflowError: + return None + except ValueError: + return None + except Exception, ex: + handle_exception() + return None + +def uurminsec(ttime): + result = "" + timeres = time.localtime(ttime) + if timeres[3] < 10: + result += "0" + str(timeres[3]) + ":" + else: + result += str(timeres[3]) + ":" + if timeres[4] < 10: + result += "0" + str(timeres[4]) + ":" + else: + result += str(timeres[4]) + ":" + if timeres[5] < 10: + result += "0" + str(timeres[5]) + else: + result += str(timeres[5]) + return result + + +def getdaymonth(ttime): + timestr = time.ctime(ttime) + result = re.search(timere, timestr) + if result: + return (result.group(3), result.group(2)) + else: + return (None, None) + +def getdaymonthyear(ttime): + timestr = time.ctime(ttime) + result = re.search(timere, timestr) + if result: + return (result.group(3), result.group(2), result.group[7]) + else: + return (None, None, None) + +def dmy(ttime): + timestr = time.ctime(ttime) + result = re.search(timere, timestr) + if result: + return "%s %s %s" % (result.group(3), result.group(2), result.group(7)) + else: + return None --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/dol.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/dol.py @@ -0,0 +1,63 @@ +# gozerbot/dol.py +# +# + +""" dict of lists """ + +__copyright__ = 'this file is in the public domain' + +class Dol(dict): + + """ dol is dict of lists """ + + def insert(self, nr, item, issue): + """ add issue to item entry """ + if self.has_key(item): + self[item].insert(nr, issue) + else: + self[item] = [issue] + return 1 + + def add(self, item, issue): + """ add issue to item entry """ + if self.has_key(item): + self[item].append(issue) + else: + self[item] = [issue] + return 1 + + def adduniq(self, item, issue): + """ only add issue to item if it is not already there """ + if self.has_key(item): + if issue in self[item]: + return 0 + self.add(item, issue) + return 1 + + def delete(self, item, number): + """ del self[item][number] """ + number = int(number) + if self.has_key(item): + try: + del self[item][number] + return 1 + except IndexError: + return None + + def remove(self, item, issue): + """ remove issue from item """ + try: + self[item].remove(issue) + return 1 + except ValueError: + pass + + def has(self, item, issue): + """ check if item has issue """ + try: + if issue in self[item]: + return 1 + else: + return None + except KeyError: + pass --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/xmpp.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/xmpp.py @@ -0,0 +1,36 @@ +# gozerbot/utils/xmpp.py +# +# + +from xml.etree import cElementTree as ET +import types + +def make_presence(xml): + pres = {'subject': '', 'to': '', 'message': '', 'name': '', 'resource': '', 'jid': ''} + if type(xml) == types.DictType: + pres.update(xml) + return pres + et = ET.XMLID(xml) + pres.update(et[0].items()) + xml = ET.XML(xml) + try: + pres['name'] = pres['from'].split('@')[0] + pres['resource'] = pres['from'].split('/')[1] + pres['jid'] = pres['from'].split('/')[0] + except KeyError: + pass + return pres + +def make_message(xml): + msg = {'subject': '', 'to': '', 'txt': '', 'message': '', 'from': '', 'jid': '', 'name': '', 'resource': ''} + if type(xml) == types.DictType: + msg.update(xml) + return msg + et = ET.XMLID(xml) + msg.update(et[0].items()) + xml = ET.XML(xml) + msg['message'] = unicode(xml.findtext('body')) + msg['name'] = msg['from'].split('@')[0] + msg['resource'] = msg['from'].split('/')[1] + msg['jid'] = msg['from'].split('/')[0] + return msg --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/limlist.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/limlist.py @@ -0,0 +1,29 @@ +# gozerbot/limlist.py +# +# + +""" limited list """ + +__copyright__ = 'this file is in the public domain' + +class Limlist(list): + + """ list with limited number of items """ + + def __init__(self, limit): + self.limit = limit + list.__init__(self) + + def insert(self, index, item): + """ insert item at index .. pop oldest item if limit is reached """ + if index > len(self): + return -1 + if len(self) >= self.limit: + self.pop(len(self)-1) + list.insert(self, index, item) + + def append(self, item): + """ add item to list .. pop oldest item if limit is reached """ + if len(self) >= self.limit: + self.pop(0) + list.append(self, item) --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/popen.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/popen.py @@ -0,0 +1,83 @@ +# gozerbot/utils/popen.py +# +# + +""" popen helper functions """ + +__copyright__ = 'this file is in the public domain' + +go = False + +try: + from subprocess import Popen, PIPE + from locking import lockdec + import thread, StringIO, logging, types + go = True +except: + go = False + +if go: + popenlock = thread.allocate_lock() + popenlocked = lockdec(popenlock) + + class PopenWhitelistError(Exception): + + def __init__(self, item): + Exception.__init__(self) + self.item = item + + def __str__(self): + return self.item + + class PopenListError(Exception): + + def __init__(self, item): + Exception.__init__(self) + self.item = item + + def __str__(self): + return str(self.item) + + class GozerStringIO(StringIO.StringIO): + + def readlines(self): + return self.read().split('\n') + + class Gozerpopen4(Popen): + + def __init__(self, args): + Popen.__init__(self, args, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + self.fromchild = self.stdout + self.tochild = self.stdin + self.errors = self.stderr + + def close(self): + self.wait() + try: + self.stdin.close() + except: + pass + try: + self.stdout.close() + except: + pass + try: + self.errors.close() + except: + pass + return self.returncode + + def gozerpopen(args, userargs=[]): + + if type(args) != types.ListType: + raise PopenListError(args) + + if type(userargs) != types.ListType: + raise PopenListError(args) + + for i in userargs: + if i.startswith('-'): + raise PopenWhitelistError(i) + + proces = Gozerpopen4(args + userargs) + return proces --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/log.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/log.py @@ -0,0 +1,104 @@ +# gozerbot/utils/log.py +# +# + +""" gozerbots logging module. logging is implemented with the use of a + loglevel and a loglist of plugins to log for. +""" + +# basic imports + +from trace import whichmodule +import logging, logging.handlers, sys, traceback, os, thread + +# basic logger +logging.basicConfig(level=logging.INFO, format="[%(asctime)s] (%(name)s) %(message)s") + +basiclogger = logging.getLogger('') + +# check if we are in GAE + +# make sure dirs are there +if not os.path.isdir('logs'): + os.mkdir('logs') +if not os.path.isdir('logs' + os.sep + 'bot'): + os.mkdir('logs' + os.sep + 'bot') + +# add logging to file +filehandler = logging.handlers.TimedRotatingFileHandler( + 'logs' + os.sep + 'bot' + os.sep + 'gozerbot.log', 'midnight') +formatter = logging.Formatter("[%(asctime)s] (%(name)s) %(message)s") +filehandler.setFormatter(formatter) +basiclogger.addHandler(filehandler) + +# the vars +loglevel = 10 +logcallbacks = [] +loglist = [] +logfile = None +enabled = True +debuglog = False + +# lock + +loglock = thread.allocate_lock() + +def exceptionmsg(): + """ create a exception traceback as 1 liner. """ + exctype, excvalue, tb = sys.exc_info() + trace = traceback.extract_tb(tb) + result = "" + for i in trace: + fname = i[0] + linenr = i[1] + func = i[2] + result += "%s:%s %s | " % (fname, linenr, func) + del trace + return "%s%s: %s" % (result, exctype, excvalue) + +def rlog(level, descr, txt): + """ log an item showing description and txt. call logcallbacks if + available + """ + if not enabled: + return + try: + #loglock.acquire() + logger = logging.getLogger(descr) + + go = False + + if loglist: + + for item in loglist: + + if item in descr: + go = True + + if level >= loglevel or loglist and go: + if debuglog: + logger.info("(%s) %s - <%s>" % (descr, txt, whichmodule(2))) + else: + logger.info(txt) + + for cb in logcallbacks: + + try: + cb(level, descr, txt) + except: + print exceptionmsg() + + except (IOError, OSError): + pass + +def disable_logging(): + global enabled + enabled = False + +def enable_logging(level=10, list=[]): + """ enable logging setting loglevel and/ort loglist """ + global loglevel, loglist, enabled + logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(name)s %(message)s") + loglevel = level + loglist = list + enabled = True --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/lazydict.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/lazydict.py @@ -0,0 +1,36 @@ +# gozerbot/utils/lazydict.py +# +# thnx to maze + +""" a lazydict allows dotted access to a dict .. dict.key """ + +class LazyDict(dict): + + """ Lazy dict allows dotted access to a dict """ + + + def __getattr__(self, attr, default={}): + + """ get attribute .. if not available init to default. """ + + if not self.has_key(attr): + self[attr] = default + + return self[attr] + + def __setattr__(self, attr, value): + + """ set attribute. """ + + self[attr] = value + + def __str__(self): + + """ return a string representation of the dict """ + + res = "" + cp = dict(self) + for item, value in cp.iteritems(): + res += "%r=%r " % (item, value) + + return res --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/nextid.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/nextid.py @@ -0,0 +1,63 @@ +# gozerbot/nextid.py +# +# + +""" provide increasing counters """ + +__copyright__ = 'this file is in the public domain' + +import os + +class Nextid(object): + + """ counters by name """ + + def __init__(self, fname): + self.data = {} + + def get(self, item): + """ get counter for item """ + item = item.lower() + try: + result = self.data[item] + except KeyError: + return None + return result + + def set(self, item, number): + """ set counter of item to number """ + item = item.lower() + try: + self.data[item] = int(number) + except ValueError: + return 0 + self.save() + return 1 + + def next(self, item): + """ get increment of item counter """ + item = item.lower() + try: + self.data[item] += 1 + except KeyError: + self.data[item] = 1 + self.save() + return self.data[item] + + def nextlist(self, item, nr): + """ get increment of item counter """ + item = item.lower() + try: + start = self.data[item] + 1 + except KeyError: + start = 1 + stop = start + nr + l = range(start, stop) + self.data[item] = stop - 1 + self.save() + return l + + def save(self): + pass + +nextid = Nextid('notused') --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/locking.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/locking.py @@ -0,0 +1,88 @@ +# gozerbot/utils/locking.py +# +# + +""" generic functions """ + +__copyright__ = 'this file is in the public domain' + +from log import rlog, loglevel +from trace import whichmodule +from lockmanager import lockmanager, rlockmanager + +import logging, sys +locks = [] + +def lockdec(lock): + + """ locking decorator. """ + + def locked(func): + + """ locking function for %s """ % str(func) + + def lockedfunc(*args, **kwargs): + """ the locked function. """ + + if loglevel <= 1: + where = whichmodule(1) + rlog(1, 'locking', 'locking on %s (%s)' % (where, str(func))) + lock.acquire() + locks.append(str(func)) + res = None + + try: + res = func(*args, **kwargs) + finally: + lock.release() + locks.remove(str(func)) + + return res + + return lockedfunc + + return locked + +def funclocked(func): + + """ locking function for %s """ % str(func) + + def lockedfunc(*args, **kwargs): + + """ the locked function. """ + + if loglevel <= 1: + where = whichmodule(1) + rlog(1, 'locking', 'locking on %s' % where) + rlockmanager.acquire(func) + locks.append(str(func)) + res = None + + try: + res = func(*args, **kwargs) + finally: + rlockmanager.release(func) + locks.remove(str(func)) + + return res + + + return lockedfunc + +class Locked(object): + + """ class used to lock an entire object. """ + + def __getattribute__(self, attr): + if loglevel <= 1: + where = whichmodule(1) + rlog(1, 'locking', 'locking on %s' % where) + rlockmanager.acquire(object) + res = None + + try: + res = super(Locked, self).__getattribute__(attr) + finally: + rlockmanager.release(object) + + return res --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/name.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/name.py @@ -0,0 +1,24 @@ +# gozerbot/utils/name.py +# +# + +""" name related helper functions. """ + +## basic imports + +import string +import os + +## defines + +allowednamechars = string.ascii_letters + string.digits + '!.@-' + +## functions + +def stripname(txt): + res = "" + for c in txt: + if c in allowednamechars: + res += c + res.replace(os.sep, '-') + return res --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/reboot.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/reboot.py @@ -0,0 +1,26 @@ +# gozerbot/utils/reboot.py +# +# + +from gozerbot.fleet import fleet +from gozerbot.config import config +from simplejson import dump +import os, sys, pickle, tempfile + +def reboot(): + fleet.exit() + os.execl(sys.argv[0], *sys.argv) + +def reboot_stateful(bot, ievent, fleet, partyline): + """ reboot the bot, but keep the connections """ + config.reload() + session = {'bots': {}, 'name': bot.name, 'channel': ievent.channel, \ +'partyline': []} + for i in fleet.bots: + session['bots'].update(i._resumedata()) + session['partyline'] = partyline._resumedata() + sessionfile = tempfile.mkstemp('-session', 'gozerbot-')[1] + dump(session, open(sessionfile, 'w')) + fleet.save() + fleet.exit(jabber=True) + os.execl(sys.argv[0], sys.argv[0], '-r', sessionfile) --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/fileutils.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/fileutils.py @@ -0,0 +1,52 @@ +# gozerbot/utils/fileutils.py +# +# Description: Various file utilities +# Author: Wijnand 'tehmaze' Modderman +# Author URL: http://tehmaze.com +# License: BSD + +from generic import istr +import tarfile, os, types, cStringIO + +def tarextract(package, fileobj=None, prefix=None, base=None): + ''' + Extracts a tarball from ``package``, or, if ``fileobj`` is either a string or a seekable + IO stream, it will extract the data from there. We only extract files from the tarball + that are member of the ``base`` directory if a ``base`` is specified. + ''' + extracted = [] + # if we have a fileobj, make sure it's a seekable stream, and not a string + if fileobj: + if type(fileobj) == types.StringType: + fileobj = cStringIO.StringIO(fileobj) + tarf = tarfile.open(mode='r|', fileobj=fileobj) + else: + tarf = tarfile.open(package, 'r') + # iterate tarball and extract candidates + for tarinfo in tarf: + if tarinfo.name.startswith('/'): + tarinfo.name = tarinfo.name[1:] # strip leading / + if not base or ((tarinfo.name.rstrip('/') == base and tarinfo.isdir()) or tarinfo.name.startswith(base+os.sep)): + if prefix: + tarinfo.name = '/'.join([prefix, tarinfo.name]) + tarf.extract(tarinfo) + extracted.append(tarinfo.name) + tarf.close() + # clean up + if fileobj: + try: + fileobj.close() + except: + pass + del fileobj + return extracted + +def bunzip2(fileobj): + import bz2 + return bz2.decompress(fileobj) + +def gunzip(fileobj): + import gzip + if type(fileobj) == types.StringType or isinstance(fileobj, istr): + fileobj = cStringIO.StringIO(str(fileobj)) + return gzip.GzipFile(mode='rb', fileobj=fileobj).read() --- gozerbot-0.99.1.orig/build/lib/gozerbot/utils/trace.py +++ gozerbot-0.99.1/build/lib/gozerbot/utils/trace.py @@ -0,0 +1,56 @@ +# gozerbot/utils/trace.py +# +# + +""" trace related functions """ + +__copyright__ = 'this file is in the public domain' + +import sys, os + +def calledfrom(frame): + try: + plugname = frame.f_back.f_code.co_filename + name = plugname.split(os.sep)[-1][:-3] + if name == '__init__': + name = plugname.split(os.sep)[-2] + if name == 'generic': + plugname = frame.f_back.f_back.f_code.co_filename + name = plugname.split(os.sep)[-1][:-3] + except AttributeError: + name = None + del frame + return name + +def callstack(frame): + result = [] + loopframe = frame + while 1: + try: + plugname = loopframe.f_back.f_code.co_filename + result.append("%s:%s" % (plugname.split(os.sep)[-1][:-3], \ +loopframe.f_back.f_lineno)) + loopframe = loopframe.f_back + except: + break + del frame + return result + +def whichmodule(depth=1): + try: + frame = sys._getframe(depth) + plugfile = frame.f_back.f_code.co_filename[:-3].split('/') + lineno = frame.f_back.f_lineno + mod = [] + for i in plugfile[::-1]: + mod.append(i) + if i == 'gozerbot' or i == 'gplugs': + break + if i == 'generic': + plugname = frame.f_back.f_back.f_code.co_filename + name = plugname.split(os.sep)[-1][:-3] + modstr = '.'.join(mod[::-1]) + ':' + str(lineno) + except AttributeError: + modstr = None + del frame + return modstr --- gozerbot-0.99.1.orig/build/lib/gozerbot/irc/bot.py +++ gozerbot-0.99.1/build/lib/gozerbot/irc/bot.py @@ -0,0 +1,721 @@ +# gozerbot/irc/bot.py +# +# +# + +""" a bot object handles the dispatching of commands and check for callbacks + that need to be fired. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.generic import getlistensocket, checkchan, waitforqueue, uniqlist, strippedtxt, makeargrest +from gozerbot.commands import cmnds +from gozerbot.callbacks import callbacks +from gozerbot.plugins import plugins +from gozerbot.users import users +from gozerbot.datadir import datadir +from gozerbot.partyline import partyline +from monitor import outmonitor +from gozerbot.channels import Channels +from gozerbot.wait import Privwait +from gozerbot.threads.thr import start_new_thread +from gozerbot.utils.dol import Dol +from gozerbot.fleet import fleet +from gozerbot.periodical import periodical +from gozerbot.persist.persiststate import PersistState +from gozerbot.runner import runners_start +from irc import Irc +from ircevent import Ircevent + +# basic imports +import re, socket, struct, Queue, time, os, types +from errno import EAGAIN + +# RE to determine if a DCC chat request is received +dccchatre = re.compile('\001DCC CHAT CHAT (\S+) (\d+)\001', re.I) + +class Bot(Irc): + + """ class that dispatches commands and checks for callbacks to fire. """ + + def __init__(self, name, cfg={}): + Irc.__init__(self, name, cfg) + # object used to wait for PRIVMSG + self.privwait = Privwait() + # channels where we are op + if not self.state.has_key('opchan'): + self.state['opchan'] = [] + self.userchannels = Dol() + self.monitor = outmonitor + self.monitor.start() + + def __str__(self): + return "name: %s nick: %s server: %s ipv6: %s ssl: %s port:%s" % (self.name, \ +self.nick, self.server, self.ipv6, self.ssl, self.port) + + def _resume(self, data, reto): + + """ resume the bot. """ + + if not Irc._resume(self, data, reto): + return 0 + for i in self.state['joinedchannels']: + periodical.addjob(15, 1, self.who, self, i) + return 1 + + def _dccresume(self, sock, nick, userhost, channel=None): + + """ resume dcc loop. """ + + if not nick or not userhost: + return + start_new_thread(self._dccloop, (sock, nick, userhost, channel)) + + def _dcclisten(self, nick, userhost, channel): + + """ accept dcc chat requests. """ + + try: + # get listen socket on host were running on + listenip = socket.gethostbyname(socket.gethostname()) + (port, listensock) = getlistensocket(listenip) + # convert ascii ip to netwerk 32 bit + ipip2 = socket.inet_aton(listenip) + ipip = struct.unpack('>L', ipip2)[0] + # send dcc chat request + chatmsg = 'DCC CHAT CHAT %s %s' % (ipip, port) + self.ctcp(nick, chatmsg) + # go listen to response + sock = listensock.accept()[0] + except Exception, ex: + rlog(10 , self.name, 'dcc error: %s' % str(ex)) + return + + # connected + self._dodcc(sock, nick, userhost, channel) + + def _dodcc(self, sock, nick, userhost, channel=None): + + """ send welcome message and loop for dcc commands. """ + + if not nick or not userhost: + return + + try: + # send welcome message .. show list of commands for USER perms + cmndslist = cmnds.list('USER') + cmndslist.sort() + sock.send('Welcome to the GOZERBOT partyline ' + nick + " ;]\n") + partylist = partyline.list_nicks() + if partylist: + sock.send("people on the partyline: %s\n" % ' .. '.join(partylist)) + sock.send("control character is ! .. bot broadcast is @\n") + except Exception, ex: + rlog(10 , self.name, 'dcc error: %s' % str(ex)) + return + start_new_thread(self._dccloop, (sock, nick, userhost, channel)) + + def _dccloop(self, sock, nick, userhost, channel=None): + + """ loop for dcc commands. """ + + sockfile = sock.makefile('r') + res = "" + # add joined user to the partyline + partyline.add_party(self, sock, nick, userhost, channel) + + while 1: + time.sleep(0.001) + try: + # read from socket + res = sockfile.readline() + # if res == "" than the otherside had disconnected + if self.stopped or not res: + rlog(1, self.name, 'closing dcc with ' + nick) + partyline.del_party(nick) + return + except socket.timeout: + # skip on timeout + continue + except socket.error, ex: + # handle socket errors .. skip on errno EAGAIN temp unavail + try: + (errno, errstr) = ex + except: + errno = 0 + errstr = str(ex) + if errno == EAGAIN: + continue + else: + raise + except Exception, ex: + # other exception occured .. close connection + handle_exception() + rlog(10, self.name, 'closing dcc with ' + nick) + partyline.del_party(nick) + return + try: + # see if user provided channel + res = strippedtxt(res.strip()) + chan = checkchan(self, res) + if chan != None: + (channel, res) = chan + else: + channel = nick + # create ircevent + ievent = Ircevent() + ievent.nick = nick + ievent.userhost = userhost + ievent.channel = channel + ievent.origtxt = res + ievent.txt = res + ievent.cmnd = 'DCC' + ievent.bot = self + ievent.sock = sock + ievent.speed = 1 + ievent.isdcc = True + ievent.msg = True + # check if its a command if so dispatch + if ievent.txt[0] == "!": + ievent.txt = ievent.txt[1:] + plugins.trydispatch(self, ievent) + continue + elif ievent.txt[0] == "@": + # command is broadcast so send response to the paryline + # members + partyline.say_broadcast_notself(ievent.nick, "[%s] %s" % (ievent.nick, ievent.txt)) + # make queue and run trydispatch to see if command has + # fired + q = Queue.Queue() + ievent.queues = [q] + ievent.txt = ievent.txt[1:] + plugins.trydispatch(self, ievent) + # wait for result .. default timeout is 10 sec + result = waitforqueue(q, 5) + if result: + # broadcast result + for i in result: + partyline.say_broadcast("[bot] %s" % i) + continue + else: + # not a command so send txt to partyline + partyline.say_broadcast_notself(ievent.nick, \ +"[%s] %s" % (ievent.nick, ievent.txt)) + # check PRIVMSG wait + self.privwait.check(ievent) + except socket.error, ex: + try: + (errno, errstr) = ex + except: + errno = 0 + errstr = str(ex) + if errno == EAGAIN: + continue + except Exception, ex: + handle_exception() + + sockfile.close() + rlog(1, self.name, 'closing dcc with ' + nick) + + def _dccconnect(self, nick, userhost, addr, port): + + """ connect to dcc request from nick. """ + + try: + port = int(port) + if re.search(':', addr): + rlog(1, self.name, 'creating ipv6 socket for dcc chat with %s'\ +% nick) + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.connect((addr, port)) + else: + rlog(1, self.name, 'creating ipv4 socket for dcc chat with %s'\ + % nick) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((addr, port)) + except Exception, ex: + rlog(10 , self.name, 'dcc error: %s' % str(ex)) + return + + # were connected .. start dcc loop + self._dodcc(sock, nick, userhost) + + def reconnect(self): + + """ reconnect and if succesfull join channels. """ + + if Irc.reconnect(self): + self.joinchannels() + + def joinchannels(self, wait=0): + + """ join channels. """ + wait = wait or self.cfg['sleepbeforejoin'] or 5 + rlog(10, self.name, "waiting %s seconds before joining channels" % wait) + time.sleep(wait) + for i in self.state['joinedchannels']: + try: + key = self.channels.getkey(i) + rlog(10, self.name, 'join %s' % i.split()[0]) + start_new_thread(self.join, (i, key)) + time.sleep(1) + except Exception, ex: + rlog(10, self.name, 'failed to join %s: %s' % (i, str(ex))) + + def broadcast(self, txt): + + """ broadcast txt to all joined channels. """ + + for i in self.state['joinedchannels']: + self.say(i, txt) + + def send(self, txt): + + """ call Irc send and check for monitor callbacks. """ + + Irc.send(self, str(txt)) + self.monitor.put(self, str(txt)) + + def save(self): + + """ saves channels and state. """ + + self.channels.save() + self.userhosts.save() + Irc.save(self) + + def stop(self): + + """ stop the bot. """ + + self.stopped = 1 + # shut down handlers + rlog(10, self.name, 'stopped') + + def exit(self): + + """ save data, quit the bot and do shutdown. """ + + if self.connectok.isSet(): + try: + self._raw('QUIT :%s' % self.cfg['quitmsg']) + except IOError: + pass + self.stop() + partyline.stop(self) + Irc.exit(self) + self.save() + rlog(10, self.name, 'exit') + return 1 + + def getchannelmode(self, channel): + + """ send MODE request for channel. """ + + if not channel: + return + self.putonqueue(9, 'MODE %s' % channel) + + def join(self, channel, password=None): + + """ join a channel .. use optional password. """ + + result = Irc.join(self, channel, password) + if result != 1: + return result + if not self.channels.has_key(channel): + # init channel data + self.channels.setdefault(channel, {}) + chan = self.channels[channel] + # if password is provided set it + got = False + if password: + chan['key'] = password + got = True + # check for control char .. if its not there init to ! + if not chan.has_key('cc'): + chan['cc'] = self.cfg['defaultcc'] or '!' + got = True + if not chan.has_key('perms'): + chan['perms'] = [] + got = True + if not chan.has_key('mode'): + chan['mode'] = "" + got = True + if got: + self.channels.save() + self.getchannelmode(channel) + return 1 + + def say(self, printto, what, who=None, how='msg', fromm=None, speed=5, groupchat=False): + + """ output what to printto. """ + + # check if printto is a queue if so put output to the queue + if type(printto) == type(Queue.Queue): + printto.put_nowait('[%s] %s' % (self.name, what)) + return + # check if bot is in notice mode + notice = False + try: + notice = self.channels[printto]['notice'] + except (KeyError, TypeError): + pass + if notice: + how = 'notice' + Irc.say(self, printto, what, who, how, fromm, speed, groupchat) + + def handle_privmsg(self, ievent): + + """ check if PRIVMSG is command, if so dispatch. """ + + if ievent.nick in self.nicks401: + rlog(10, self.name, "%s is available again" % ievent.nick) + self.nicks401.remove(ievent.nick) + + if not ievent.txt: + return + + # check if it is a dcc chat request + chat = re.search(dccchatre, ievent.txt) + if chat: + # check if the user is known + if users.allowed(ievent.userhost, 'USER'): + # start connection + start_new_thread(self._dccconnect, (ievent.nick, ievent.userhost, chat.group(1), chat.group(2))) + return + + # see if base class method would handle it + if '\001' in ievent.txt: + Irc.handle_privmsg(self, ievent) + return + + # set bot and socket in ircevent + ievent.bot = self + ievent.sock = self.sock + chan = ievent.channel + + # check for /msg + if chan == self.nick: + ievent.msg = 1 + ievent.speed = 7 + ievent.printto = ievent.nick + ccs = ['!', '@', self.cfg['defaultcc']] + # check for PRIVMSG waiting callback + self.privwait.check(ievent) + if ievent.isresponse: + return + if not self.cfg['noccinmsg']: + plugins.trydispatch(self, ievent) + elif ievent.txt[0] in ccs: + ievent.txt = ievent.txt[1:] + plugins.trydispatch(self, ievent) + return + + ievent.printto = chan + + # see if we can get channel control character + try: + cchar = self.channels[chan]['cc'] + except LookupError: + cchar = self.cfg['defaultcc'] or '!' + except TypeError: + cchar = self.cfg['defaultcc'] or '!' + + # see if cchar matches, if so dispatch + ievent.speed = 5 + if ievent.txt[0] in cchar: + ievent.cc = ievent.txt[0] + ievent.txt = ievent.txt[1:] + plugins.trydispatch(self, ievent) + return + + # see if were adressed, if so dispatch + txtlist = ievent.txt.split(':', 1) + if txtlist[0] == self.nick: + if len(txtlist) < 2: + return + ievent.txt = txtlist[1].strip() + plugins.trydispatch(self, ievent) + return + + # habbie addressing mode + txtlist = ievent.txt.split(',', 1) + if txtlist[0] == self.nick: + if len(txtlist) < 2: + return + ievent.txt = txtlist[1].strip() + plugins.trydispatch(self, ievent) + return + + # check for PRIVMSG waiting callback + self.privwait.check(ievent) + + def handle_join(self, ievent): + + """ handle joins. """ + + if ievent.nick in self.nicks401: + rlog(10, self.name, "%s is available again" % ievent.nick) + self.nicks401.remove(ievent.nick) + chan = ievent.channel + nick = ievent.nick + + # see if its the bot who is joining + if nick == self.nick: + # check if we already have a channels object, if not init it + if not self.channels.has_key(chan): + self.channels[chan] = {} + self.channels[chan]['cc'] = self.cfg['defaultcc'] or '!' + if not chan in self.state['joinedchannels']: + self.state['joinedchannels'].append(chan) + self.state.save() + if chan in self.state['opchan']: + self.state['opchan'].remove(chan) + self.state.save() + self.timejoined[chan] = time.time() + time.sleep(0.5) + periodical.addjob(10, 1, self.who, self, chan) + return + + # sync joined user with userhosts cache + self.userhosts.data[nick] = ievent.userhost + self.userchannels.adduniq(nick, chan) + + def handle_kick(self, ievent): + + """ handle kick event. """ + + try: + who = ievent.arguments[1] + except IndexError: + return + + chan = ievent.channel + + # see if its the bot who got kicked .. if so remove from + # joinedchannels + if who == self.nick: + if chan in self.state['joinedchannels']: + self.state['joinedchannels'].remove(chan) + self.state.save() + + def handle_nick(self, ievent): + + """ update userhost cache on nick change. """ + + nick = ievent.txt + self.userhosts.data[nick] = ievent.userhost + + if ievent.nick == self.nick: + self.cfg['nick'] = nick + self.cfg.save() + + try: + self.userchannels[nick] = self.userchannels[ievent.nick] + except: + raise + + def handle_part(self, ievent): + + """ handle parts. """ + + chan = ievent.channel + + # see if its the bot who is parting + if ievent.nick == self.nick: + rlog(1, self.name, 'parted channel %s' % chan) + # remove from joinedchannels + if chan in self.state['joinedchannels']: + self.state['joinedchannels'].remove(chan) + self.state.save() + + def handle_ievent(self, ievent): + + """ check for callbacks, call Irc method. """ + + try: + # call parent method + Irc.handle_ievent(self, ievent) + # check for callbacks + if ievent.cmnd == 'JOIN' or ievent.msg: + if ievent.nick in self.nicks401: + self.nicks401.remove(ievent.nick) + i = Ircevent() + i.copyin(ievent) + i.bot = self + i.sock = self.sock + callbacks.check(self, i) + except: + handle_exception() + + def handle_quit(self, ievent): + + """ check if quit is because of a split. """ + + if '*.' in ievent.txt or self.server in ievent.txt: + self.splitted.append(ievent.nick) + + def handle_mode(self, ievent): + + """ check if mode is about channel if so request channel mode. """ + + rlog(5, self.name, "mode change %s" % str(ievent.arguments)) + + try: + dummy = ievent.arguments[2] + except IndexError: + chan = ievent.channel + # channel mode change has 2 arguments + self.getchannelmode(chan) + self.channels.set(chan, 'mode', ievent.arguments[1]) + + def handle_311(self, ievent): + + """ handle 311 response .. sync with userhosts cache. """ + + target, nick, user, host, dummy = ievent.arguments + nick = nick + userhost = "%s@%s" % (user, host) + rlog(1, self.name, 'adding %s to userhosts: %s' % (nick, userhost)) + # userhosts cache is accessed by lower case nick + self.userhosts.data[nick] = userhost + + def handle_352(self, ievent): + + """ handle 352 response .. sync with userhosts cache. """ + + args = ievent.arguments + channel = args[1] + nick = args[5] + user = args[2] + host = args[3] + userhost = "%s@%s" % (user, host) + rlog(1, self.name, 'adding %s to userhosts: %s' % (nick, userhost)) + self.userhosts.data[nick] = userhost + self.userchannels.adduniq(nick, channel) + + def handle_353(self, ievent): + + """ handle 353 .. check if we are op. """ + + userlist = ievent.txt.split() + chan = ievent.channel + for i in userlist: + if i[0] == '@' and i[1:] == self.nick: + if chan not in self.state['opchan']: + self.state['opchan'].append(chan) + + def handle_324(self, ievent): + + """ handle mode request responses. """ + + self.channels.set(ievent.channel, 'mode', ievent.arguments[2]) + + def handle_invite(self, ievent): + + """ join channel if invited by OPER. """ + + if users.allowed(ievent.userhost, ['OPER', ]): + self.join(ievent.txt) + + def settopic(self, channel, txt): + + """ set topic of channel to txt. """ + + if not channel or not txt: + return + self.putonqueue(7, 'TOPIC %s :%s' % (channel, txt)) + + def gettopic(self, channel): + + """ get topic data. """ + + if not channel: + return + + queue332 = Queue.Queue() + queue333 = Queue.Queue() + self.wait.register('332', channel, queue332) + self.wait.register('333', channel, queue333) + self.putonqueue(7, 'TOPIC %s' % channel) + + try: + res = queue332.get(1, 5) + except Queue.Empty: + return None + + what = res.txt + + try: + res = queue333.get(1, 5) + except Queue.Empty: + return None + + try: + splitted = res.postfix.split() + who = splitted[2] + when = float(splitted[3]) + except (IndexError, ValueError): + return None + + return (what, who, when) + + def test(self, txt, timeout=0, kw={}): + + """ run txt with test ievent. """ + + txt = txt.strip() + ievent = Ircevent() + ievent.bot = self + ievent.cmnd = 'PRIVMSG' + ievent.nick = 'test' + ievent.userhost = 'test@test' + ievent.origtxt = txt + ievent.txt = ievent.origtxt + ievent.channel = '#test' + ievent.allowqueue = True + ievent.closequeue = True + + if kw: + for i, j in kw.iteritems(): + setattr(ievent, i, j) + + if timeout: + result = plugins.cmnd(self, ievent, timeout) + else: + result = plugins.cmnd(self, ievent, 10) + + rlog(1000, self.name, str(result)) + return result + + def testasync(self, txt, timeout=0, kw={}): + + """ run txt with test ievent. don't close queues on exit.""" + + txt = txt.strip() + ievent = Ircevent() + ievent.bot = self + ievent.cmnd = 'PRIVMSG' + ievent.nick = 'test' + ievent.userhost = 'test@test' + ievent.origtxt = txt + ievent.txt = ievent.origtxt + ievent.channel = '#test' + ievent.allowqueue = False + ievent.closequeue = False + + if kw: + for i, j in kw.iteritems(): + setattr(ievent, i, j) + + if timeout: + result = plugins.cmnd(self, ievent, timeout) + else: + result = plugins.cmnd(self, ievent, 10) + + rlog(100, self.name, str(result)) + return result --- gozerbot-0.99.1.orig/build/lib/gozerbot/irc/monitor.py +++ gozerbot-0.99.1/build/lib/gozerbot/irc/monitor.py @@ -0,0 +1,41 @@ +# gozerbot/monitor.py +# +# + +""" monitors .. call callback on bot output. """ + +__copyright__ = 'this file is in the public domain' + +from gozerbot.monitor import Monitor + +# gozerbot imports +from ircevent import Ircevent + +class Outmonitor(Monitor): + + """ monitor for bot output (bot.send). """ + + def handle(self, bot, txt): + + """ fire outmonitor callbacks. """ + + ievent = Ircevent().parse(bot, txt) + + if not ievent: + rlog(10, 'monitor', "can't make ircevent: %s" % txt) + return + + ievent.nick = bot.nick + + try: + ievent.userhost = bot.userhosts.data[bot.nick] + except KeyError: + ievent.userhost = "bot@bot" + + Monitor.handle(self, bot, ievent) + +# bot.send() monitor +outmonitor = Outmonitor('outmonitor') + +# bot.say() monitor +saymonitor = Monitor('saymonitor') --- gozerbot-0.99.1.orig/build/lib/gozerbot/irc/irc.py +++ gozerbot-0.99.1/build/lib/gozerbot/irc/irc.py @@ -0,0 +1,1144 @@ +# gozerbot/irc.py +# +# + +""" an Irc object handles the connection to the irc server .. receiving, + sending, connect and reconnect code. +""" + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.generic import getrandomnick, toenc, fromenc, strippedtxt +from gozerbot.utils.generic import fix_format, splittxt, waitforqueue, uniqlist +from gozerbot.utils.locking import lockdec +from gozerbot.wait import Wait +from gozerbot.config import config +from monitor import saymonitor +from gozerbot.less import Less +from gozerbot.ignore import shouldignore +from gozerbot.persist.pdod import Pdod +from gozerbot.datadir import datadir +#from gozerbot.fleet import fleet +from gozerbot.botbase import BotBase +from gozerbot.threads.thr import start_new_thread, threaded +from gozerbot.periodical import periodical +from gozerbot.morphs import inputmorphs, outputmorphs +from ircevent import Ircevent + +# basic imports +import time, thread, socket, threading, os, Queue, random +from errno import EAGAIN, EBADF, EPIPE + +# locks +outlock = thread.allocate_lock() +outlocked = lockdec(outlock) + +# exceptions + +class AlreadyConnected(Exception): + + """ already connected exception """ + + pass + +class AlreadyConnecting(Exception): + + """ bot is already connecting exception """ + + pass + +class Irc(BotBase): + + """ the irc class, provides interface to irc related stuff. """ + + def __init__(self, name, cfg={}): + BotBase.__init__(self, name, cfg) + self.type = 'irc' + self.wait = Wait() + self.outputlock = thread.allocate_lock() + self.fsock = None + self.oldsock = None + self.sock = None + if not self.cfg.has_key('nolimiter'): + self.nolimiter = 0 + else: + self.nolimiter = self.cfg['nolimiter'] + self.reconnectcount = 0 + self.pongcheck = 0 + self.nickchanged = 0 + self.noauto433 = 0 + if not self.state.has_key('alternick'): + self.state['alternick'] = self.cfg['alternick'] + if not self.state.has_key('no-op'): + self.state['no-op'] = [] + self.nrevents = 0 + self.gcevents = 0 + self.outqueues = [Queue.Queue() for i in range(10)] + self.tickqueue = Queue.Queue() + self.nicks401 = [] + self.stopreadloop = False + self.stopoutloop = False + self.port = self.cfg['port'] or 6667 + rlog(10, self.name, "port is %s" % self.port) + self.connectlock = thread.allocate_lock() + self.encoding = 'utf-8' + + def __del__(self): + self.exit() + + def _raw(self, txt): + + """ send raw text to the server. """ + + if not txt: + return + + rlog(2, self.name + '.sending', txt) + + try: + self.lastoutput = time.time() + itxt = toenc(outputmorphs.do(txt), self.encoding) + if self.cfg.has_key('ssl') and self.cfg['ssl']: + self.sock.write(itxt + '\n') + else: + self.sock.send(itxt[:500] + '\n') + except Exception, ex: + # check for broken pipe error .. if so ignore + # used for nonblocking sockets + try: + (errno, errstr) = ex + if errno != EPIPE and errno != EBADF: + raise + else: + time.sleep(0.5) + except: + rlog(10, self.name, "ERROR: can't send %s" % str(ex)) + self.reconnect() + + def _connect(self): + + """ connect to server/port using nick. """ + + if self.connecting: + rlog(10, self.name, 'already connecting') + raise AlreadyConnecting() + + if self.connected: + rlog(10, self.name, 'already connected') + raise AlreadyConnected() + + self.stopped = 0 + self.connecting = True + self.connectok.clear() + self.connectlock.acquire() + + # create socket + if self.ipv6: + rlog(10, self.name, 'creating ipv6 socket') + self.oldsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + self.ipv6 = 1 + else: + rlog(10, self.name, 'creating ipv4 socket') + self.oldsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + assert(self.oldsock) + + # optional bind + server = self.server + elite = self.cfg['bindhost'] or config['bindhost'] + if elite: + try: + self.oldsock.bind((elite, 0)) + except socket.gaierror: + rlog(10, self.name, "can't bind to %s" % elite) + # resolve the IRC server and pick a random server + if not server: + # valid IPv6 ip? + try: socket.inet_pton(socket.AF_INET6, self.server) + except socket.error: pass + else: server = self.server + if not server: + # valid IPv4 ip? + try: socket.inet_pton(socket.AF_INET, self.server) + except socket.error: pass + else: server = self.server + if not server: + # valid hostname? + ips = [] + try: + for item in socket.getaddrinfo(self.server, None): + if item[0] in [socket.AF_INET, socket.AF_INET6] and item[1] == socket.SOCK_STREAM: + ip = item[4][0] + if ip not in ips: ips.append(ip) + except socket.error: pass + else: server = random.choice(ips) + + # do the connect .. set timeout to 30 sec upon connecting + rlog(10, self.name, 'connecting to %s (%s)' % (server, self.server)) + self.oldsock.settimeout(15) + self.oldsock.connect((server, int(self.port))) + + # we are connected + rlog(10, self.name, 'connection ok') + time.sleep(1) + self.connected = True + + # make file socket + self.fsock = self.oldsock.makefile("r") + + # set blocking + self.oldsock.setblocking(self.blocking) + self.fsock._sock.setblocking(self.blocking) + + # set socket time out + if self.blocking: + socktimeout = self.cfg['socktimeout'] + if not socktimeout: + socktimeout = 301.0 + else: + socktimeout = float(socktimeout) + self.oldsock.settimeout(socktimeout) + self.fsock._sock.settimeout(socktimeout) + # enable ssl if set + if self.cfg.has_key('ssl') and self.cfg['ssl']: + rlog(10, self.name, 'ssl enabled') + try: + import ssl + rlog(10, self.name, 'using ssl.wrap_socket()') + self.sock = ssl.wrap_socket(self.oldsock) + except Exception, ex: + rlog(10, self.name, 'using socket.ssl()') + self.sock = socket.ssl(self.oldsock) + else: self.sock = self.oldsock + + # try to release the outputlock + try: + self.outputlock.release() + except thread.error: + pass + + # start input and output loops + start_new_thread(self._readloop, ()) + start_new_thread(self._outloop, ()) + + # logon and start monitor + self._logon() + self.nickchanged = 0 + self.reconnectcount = 0 + saymonitor.start() + return 1 + + def _readloop(self): + + """ loop on the socketfile. """ + + self.stopreadloop = 0 + self.stopped = 0 + doreconnect = 0 + timeout = 1 + rlog(5, self.name, 'starting readloop') + prevtxt = "" + + while not self.stopped and not self.stopreadloop: + + try: + time.sleep(0.01) + if self.cfg.has_key('ssl') and self.cfg['ssl']: + intxt = inputmorphs.do(self.sock.read()).split('\n') + else: + intxt = inputmorphs.do(self.fsock.readline()).split('\n') + # if intxt == "" the other side has disconnected + if self.stopreadloop or self.stopped: + doreconnect = 0 + break + if not intxt or not intxt[0]: + doreconnect = 1 + break + if prevtxt: + intxt[0] = prevtxt + intxt[0] + prevtxt = "" + if intxt[-1] != '': + prevtxt = intxt[-1] + intxt = intxt[:-1] + for r in intxt: + r = r.rstrip() + rr = fromenc(r, self.encoding) + if not rr: + continue + res = strippedtxt(rr) + res = rr + rlog(2, self.name, res) + # parse txt read into an ircevent + try: + ievent = Ircevent().parse(self, res) + except Exception, ex: + handle_exception() + continue + # call handle_ievent + if ievent: + self.handle_ievent(ievent) + timeout = 1 + + except UnicodeError: + handle_exception() + continue + + except socket.timeout: + # timeout occured .. first time send ping .. reconnect if + # second timeout follows + if self.stopped: + break + timeout += 1 + if timeout > 2: + doreconnect = 1 + rlog(10, self.name, 'no pong received') + break + rlog(1, self.name, "socket timeout") + pingsend = self.ping() + if not pingsend: + doreconnect = 1 + break + continue + + except socket.sslerror, ex: + # timeout occured .. first time send ping .. reconnect if + # second timeout follows + if self.stopped or self.stopreadloop: + break + if not 'timed out' in str(ex): + handle_exception() + doreconnect = 1 + break + timeout += 1 + if timeout > 2: + doreconnect = 1 + rlog(10, self.name, 'no pong received') + break + rlog(1, self.name, "socket timeout") + pingsend = self.ping() + if not pingsend: + doreconnect = 1 + break + continue + + except IOError, ex: + if self.blocking and 'temporarily' in str(ex): + time.sleep(0.5) + continue + handle_exception() + doreconnect = 1 + break + + except Exception, ex: + if self.stopped or self.stopreadloop: + break + err = ex + try: + (errno, msg) = ex + except: + errno = -1 + msg = err + # check for temp. unavailable error .. raised when using + # nonblocking socket .. + if errno == EAGAIN: + time.sleep(0.5) + continue + rlog(10, self.name, "error in readloop: %s" % msg) + doreconnect = 1 + break + + rlog(5, self.name, 'readloop stopped') + self.connectok.clear() + self.connected = False + + # see if we need to reconnect + if doreconnect: + time.sleep(2) + self.reconnect() + + def _getqueue(self): + + """ get one of the outqueues. """ + + go = self.tickqueue.get() + for index in range(len(self.outqueues)): + if not self.outqueues[index].empty(): + return self.outqueues[index] + + def putonqueue(self, nr, *args): + + """ put output onto one of the output queues. """ + + self.outqueues[nr].put_nowait(*args) + self.tickqueue.put_nowait('go') + + def _outloop(self): + + """ output loop. """ + + rlog(5, self.name, 'starting output loop') + self.stopoutloop = 0 + + while not self.stopped and not self.stopoutloop: + queue = self._getqueue() + if queue: + rlog(5, self.name, "outputsizes: %s" % self.outputsizes()) + try: + res = queue.get_nowait() + except Queue.Empty: + continue + if not res: + continue + try: + (printto, what, who, how, fromm, speed) = res + except ValueError: + self.send(res) + continue + if not self.stopped and not self.stopoutloop and printto \ +not in self.nicks401: + self.out(printto, what, who, how, fromm, speed) + else: + time.sleep(0.1) + + rlog(5, self.name, 'stopping output loop') + + def _logon(self): + + """ log on to the network. """ + + # if password is provided send it + if self.password: + rlog(10, self.name ,'sending password') + self._raw("PASS %s" % self.password) + + # register with irc server + rlog(10, self.name, 'registering with %s using nick %s' % \ +(self.server, self.nick)) + rlog(10, self.name, 'this may take a while') + + # check for username and realname + username = self.nick or self.cfg['username'] + realname = self.cfg['realname'] or username + + # first send nick + time.sleep(1) + self._raw("NICK %s" % self.nick) + time.sleep(1) + + # send USER + self._raw("USER %s localhost localhost :%s" % (username, \ +realname)) + + # wait on login + self.connectok.wait() + + def _onconnect(self): + + """ overload this to run after connect. """ + + pass + + def _resume(self, data, reto=None): + + """ resume to server/port using nick. """ + + try: + if data['ssl']: + self.connectwithjoin() + return 1 + except KeyError: + pass + + try: + fd = int(data['fd']) + except (TypeError, ValueError): + fd = None + + self.connecting = False # we're already connected + self.nick = data['nick'] + self.orignick = self.nick + self.server = str(data['server']) + self.port = int(data['port']) + self.password = data['password'] + self.ipv6 = data['ipv6'] + self.ssl = data['ssl'] + + # create socket + if self.ipv6: + if fd: + rlog(1, self.name, 'resuming ipv6 socket') + self.sock = socket.fromfd(fd , socket.AF_INET6, socket.SOCK_STREAM) + else: + rlog(10, self.name, 'creating ipv6 socket') + self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + self.ipv6 = 1 + else: + if fd: + rlog(1, self.name, 'resuming ipv4 socket') + self.sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) + else: + rlog(10, self.name, 'creating ipv4 socket') + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # do the connect .. set timeout to 30 sec upon connecting + rlog(10, self.name, 'resuming to ' + self.server) + self.sock.settimeout(30) + + # we are connected + rlog(10, self.name, 'connection ok') + self.stopped = 0 + # make file socket + self.fsock = self.sock.makefile("r") + # set blocking + self.sock.setblocking(self.blocking) + + # set socket time out + if self.blocking: + socktimeout = self.cfg['socktimeout'] + if not socktimeout: + socktimeout = 301.0 + else: + socktimeout = float(socktimeout) + self.sock.settimeout(socktimeout) + + # start readloop + rlog(0, self.name, 'resuming readloop') + start_new_thread(self._readloop, ()) + start_new_thread(self._outloop, ()) + + # init + self.reconnectcount = 0 + self.nickchanged = 0 + self.connecting = False + + # still there server? + self._raw('PING :RESUME %s' % str(time.time())) + self.connectok.set() + self.connected = True + self.reconnectcount = 0 + if reto: + self.say(reto, 'rebooting done') + saymonitor.start() + return 1 + + def _resumedata(self): + + """ return data used for resume. """ + try: + fd = self.sock.fileno() + except AttributeError, ex: + fd = None + self.exit() + return {self.name: { + 'nick': self.nick, + 'server': self.server, + 'port': self.port, + 'password': self.password, + 'ipv6': self.ipv6, + 'ssl': self.ssl, + 'fd': fd + }} + + + def outputsizes(self): + + """ return sizes of output queues. """ + + result = [] + for q in self.outqueues: + result.append(q.qsize()) + return result + + def broadcast(self, txt): + + """ broadcast txt to all joined channels. """ + + for i in self.state['joinedchannels']: + self.say(i, txt, speed=-1) + + def save(self): + + """ save state data. """ + + self.state.save() + + def connect(self, reconnect=True): + + """ connect to server/port using nick .. connect can timeout so catch + exception .. reconnect if enabled. + """ + + res = 0 + + try: + res = self._connect() + if res: + self.connectok.wait() + self._onconnect() + self.connected = True + rlog(10, self.name, 'logged on !') + self.connecting = False + except AlreadyConnecting: + return 0 + except AlreadyConnected: + return 0 + except Exception, ex: + self.connectlock.release() + if self.stopped: + return 0 + rlog(10, self.name, 'connecting error: %s' % str(ex)) + if reconnect: + self.reconnect() + return + raise + + # add bot to the fleet + #if not fleet.byname(self.name): + # fleet.addbot(self) + self.connectlock.release() + return res + + def shutdown(self): + + """ shutdown the bot. """ + + rlog(10, self.name, 'shutdown') + self.stopoutputloop = 1 + self.stopped = 1 + time.sleep(1) + self.tickqueue.put_nowait('go') + self.close() + self.connecting = False + self.connected = False + self.connectok.clear() + + def close(self): + + """ close the connection. """ + + try: + if self.cfg.has_key('ssl') and self.cfg['ssl']: + self.oldsock.shutdown(2) + else: + self.sock.shutdown(2) + except: + pass + try: + if self.cfg.has_key('ssl') and self.cfg['ssl']: + self.oldsock.close() + else: + self.sock.close() + self.fsock.close() + except: + pass + + def exit(self): + + """ exit the bot. """ + + self.stopreadloop = 1 + self.stopped = 1 + self.connected = 0 + self.shutdown() + + def reconnect(self): + + """ reconnect to the irc server. """ + + try: + if self.stopped: + return 0 + # determine how many seconds to sleep + if self.reconnectcount > 0: + reconsleep = self.reconnectcount*15 + rlog(10, self.name, 'sleeping %s seconds for reconnect' % \ +reconsleep) + time.sleep(reconsleep) + if self.stopped: + rlog(10, self.name, 'stopped.. not reconnecting') + return 1 + if self.connected: + rlog(10, self.name, 'already connected .. not reconnecting') + return 1 + self.reconnectcount += 1 + self.exit() + rlog(10, self.name, 'reconnecting') + result = self.connect() + return result + except Exception, ex: + handle_exception() + + def handle_pong(self, ievent): + + """ set pongcheck on received pong. """ + + rlog(1, self.name, 'received server pong') + self.pongcheck = 1 + + def sendraw(self, txt): + + """ send raw text to the server. """ + + if self.stopped: + return + rlog(2, self.name + '.sending', txt) + self._raw(txt) + + def fakein(self, txt): + + """ do a fake ircevent. """ + + if not txt: + return + rlog(10, self.name + '.fakein', txt) + self.handle_ievent(Ircevent().parse(self, txt)) + + def say(self, printto, what, who=None, how='msg', fromm=None, speed=0, groupchat=False): + + """ say what to printto. """ + + if not printto or not what or printto in self.nicks401: + return + + # if who is set add "who: " to txt + if not 'socket' in repr(printto): + if who: + what = "%s: %s" % (who, what) + if speed > 9: + speed = 9 + self.putonqueue(9-speed, (printto, what, who, how, fromm, speed)) + return + + # do the sending + try: + printto.send(what + '\n') + time.sleep(0.001) + except Exception, ex : + if "Broken pipe" in str(ex) or "Bad file descriptor" in str(ex): + return + handle_exception() + + def out(self, printto, what, who=None, how='msg', fromm=None, speed=5): + + """ output the first 375 chars .. put the rest into cache. """ + + # convert the data to the encoding + try: + what = toenc(what.rstrip()) + except Exception, ex: + rlog(10, self.name, "can't output: %s" % str(ex)) + return + if not what: + return + + # split up in parts of 375 chars overflowing on word boundaries + txtlist = splittxt(what) + size = 0 + + # send first block + self.output(printto, txtlist[0], how, who, fromm) + + # see if we need to store output in less cache + result = "" + if len(txtlist) > 2: + if not fromm: + self.less.add(printto, txtlist[1:]) + else: + self.less.add(fromm, txtlist[1:]) + size = len(txtlist) - 2 + result = txtlist[1:2][0] + if size: + result += " (+%s)" % size + else: + if len(txtlist) == 2: + result = txtlist[1] + + # send second block + if result: + self.output(printto, result, how, who, fromm) + + def output(self, printto, what, how='msg' , who=None, fromm=None): + + """ first output .. then call saymonitor. """ + + self.outputnolog(printto, what, how, who, fromm) + saymonitor.put(self.name, printto, what, who, how, fromm) + + def outputnolog(self, printto, what, how, who=None, fromm=None): + + """ do output to irc server .. rate limit to 3 sec. """ + + if fromm and shouldignore(fromm): + return + + try: + what = fix_format(what) + if what: + if how == 'msg': + self.privmsg(printto, what) + elif how == 'notice': + self.notice(printto, what) + elif how == 'ctcp': + self.ctcp(printto, what) + except Exception, ex: + handle_exception() + + def donick(self, nick, setorig=0, save=0, whois=0): + + """ change nick .. optionally set original nick and/or save to config. """ + + if not nick: + return + + # disable auto 433 nick changing + self.noauto433 = 1 + + # set up wait for NICK command and issue NICK + queue = Queue.Queue() + nick = nick[:16] + self.wait.register('NICK', self.nick[:16], queue, 12) + self._raw('NICK %s\n' % nick) + result = waitforqueue(queue, 5) + + # reenable 433 auto nick changing + self.noauto433 = 0 + if not result: + return 0 + self.nick = nick + + # send whois + if whois: + self.whois(nick) + + # set original + if setorig: + self.orignick = nick + + # save nick to state and config file + if save: + self.state['nick'] = nick + self.state.save() + self.cfg.set('nick', nick) + self.cfg.save() + return 1 + + def join(self, channel, password=None): + + """ join channel with optional password. """ + + if not channel: + return + + # do join with password + if password: + self._raw('JOIN %s %s' % (channel, password)) + try: + self.channels[channel]['key'] = password + self.channels.save() + except KeyError: + pass + else: + # do pure join + self._raw('JOIN %s' % channel) + + def part(self, channel): + + """ leave channel. """ + + if not channel: + return + self._raw('PART %s' % channel) + + try: + self.state['joinedchannels'].remove(channel) + self.state.save() + except (KeyError, ValueError): + pass + + def who(self, who): + + """ send who query. """ + + if not who: + return + self.putonqueue(6, 'WHO %s' % who.strip()) + + def names(self, channel): + + """ send names query. """ + + if not channel: + return + self.putonqueue(6, 'NAMES %s' % channel) + + def whois(self, who): + + """ send whois query. """ + + if not who: + return + self.putonqueue(6, 'WHOIS %s' % who) + + def privmsg(self, printto, what): + + """ send privmsg to irc server. """ + + if not printto or not what: + return + self.send('PRIVMSG %s :%s' % (printto, what)) + + def send(self, txt): + + """ send text to irc server. """ + + if not txt: + return + + if self.stopped: + return + + try: + self.outputlock.acquire() + txt = toenc(strippedtxt(txt)) + txt = txt.rstrip() + self._raw(txt) + now = time.time() + timetosleep = 5 - (now - self.lastoutput) + if timetosleep > 0 and not self.nolimiter: + rlog(0, self.name, 'flood protect') + time.sleep(timetosleep) + try: + self.outputlock.release() + except: + pass + self.lastoutput = time.time() + except Exception, ex: + try: + self.outputlock.release() + except: + pass + if not self.blocking and 'broken pipe' in str(ex).lower(): + time.sleep(0.5) + else: + rlog(11, self.name, 'send error: %s' % str(ex)) + self.reconnect() + return + + def voice(self, channel, who): + + """ give voice. """ + + if not channel or not who: + return + self.putonqueue(9, 'MODE %s +v %s' % (channel, who)) + + def doop(self, channel, who): + + """ give ops. """ + + if not channel or not who: + return + self._raw('MODE %s +o %s' % (channel, who)) + + def delop(self, channel, who): + + """ de-op user. """ + + if not channel or not who: + return + self._raw('MODE %s -o %s' % (channel, who)) + + def quit(self, reason='http://gozerbot.org'): + + """ send quit message. """ + + rlog(10, self.name, 'sending quit') + try: + self._raw('QUIT :%s' % reason) + except IOError: + pass + + def notice(self, printto, what): + + """ send notice. """ + + if not printto or not what: + return + self.send('NOTICE %s :%s' % (printto, what)) + + def ctcp(self, printto, what): + + """ send ctcp privmsg. """ + + if not printto or not what: + return + self.send("PRIVMSG %s :\001%s\001" % (printto, what)) + + def ctcpreply(self, printto, what): + + """ send ctcp notice. """ + + if not printto or not what: + return + self.putonqueue(2, "NOTICE %s :\001%s\001" % (printto, what)) + + def action(self, printto, what): + + """ do action. """ + + if not printto or not what: + return + self.putonqueue(9, "PRIVMSG %s :\001ACTION %s\001" % (printto, what)) + + def handle_ievent(self, ievent): + + """ handle ircevent .. dispatch to 'handle_command' method. """ + + try: + if ievent.cmnd == 'JOIN' or ievent.msg: + if ievent.nick in self.nicks401: + self.nicks401.remove(ievent.nick) + rlog(10, self.name, '%s joined .. unignoring' % ievent.nick) + # see if the irc object has a method to handle the ievent + method = getattr(self,'handle_' + ievent.cmnd.lower()) + # try to call method + if method: + try: + method(ievent) + except: + handle_exception() + except AttributeError: + # no command method to handle event + pass + try: + # see if there are wait callbacks + self.wait.check(ievent) + except: + handle_exception() + + def handle_432(self, ievent): + + """ erroneous nick. """ + + self.handle_433(ievent) + + def handle_433(self, ievent): + + """ handle nick already taken. """ + + if self.noauto433: + return + nick = ievent.arguments[1] + # check for alternick + alternick = self.state['alternick'] + if alternick and not self.nickchanged: + rlog(10, self.name, 'using alternick %s' % alternick) + self.donick(alternick) + self.nickchanged = 1 + return + # use random nick + randomnick = getrandomnick() + self._raw("NICK %s" % randomnick) + self.nick = randomnick + rlog(100, self.name, 'ALERT: nick %s already in use/unavailable .. \ +using randomnick %s' % (nick, randomnick)) + self.nickchanged = 1 + + def handle_ping(self, ievent): + + """ send pong response. """ + + if not ievent.txt: + return + self._raw('PONG :%s' % ievent.txt) + + def handle_001(self, ievent): + + """ we are connected. """ + + self.connectok.set() + self.connected = True + periodical.addjob(15, 1, self.whois, self, self.nick) + + def handle_privmsg(self, ievent): + + """ check if msg is ctcp or not .. return 1 on handling. """ + + if ievent.txt and ievent.txt[0] == '\001': + self.handle_ctcp(ievent) + return 1 + + def handle_notice(self, ievent): + + """ handle notice event .. check for version request. """ + + if ievent.txt and ievent.txt.find('VERSION') != -1: + self.say(ievent.nick, self.cfg['version'], None, 'notice') + return 1 + + def handle_ctcp(self, ievent): + + """ handle client to client request .. version and ping. """ + + if ievent.txt.find('VERSION') != -1: + self.ctcpreply(ievent.nick, 'VERSION %s' % self.cfg['version']) + + if ievent.txt.find('PING') != -1: + try: + pingtime = ievent.txt.split()[1] + pingtime2 = ievent.txt.split()[2] + if pingtime: + self.ctcpreply(ievent.nick, 'PING ' + pingtime + ' ' + \ +pingtime2) + except IndexError: + pass + + def handle_error(self, ievent): + + """ show error. """ + + if ievent.txt.startswith('Closing'): + rlog(10, self.name, ievent.txt) + else: + rlog(10, self.name + '.ERROR', "%s - %s" % (ievent.arguments, \ +ievent.txt)) + + def ping(self): + + """ ping the irc server. """ + + rlog(1, self.name, 'sending ping') + try: + self.putonqueue(1, 'PING :%s' % self.server) + return 1 + except Exception, ex: + rlog(10, self.name, "can't send ping: %s" % str(ex)) + return 0 + + def handle_401(self, ievent): + + """ handle 401 .. nick not available. """ + + try: + nick = ievent.arguments[1] + if nick not in self.nicks401: + rlog(10, self.name, '401 on %s .. ignoring' % nick) + self.nicks401.append(nick) + except: + pass + + handle_403 = handle_401 + + + def handle_700(self, ievent): + + """ handle 700 .. encoding request of the server. """ + + try: + self.encoding = ievent.arguments[1] + rlog(10, self.name, '700 encoding now is %s' % self.encoding) + except: + pass --- gozerbot-0.99.1.orig/build/lib/gozerbot/irc/ircevent.py +++ gozerbot-0.99.1/build/lib/gozerbot/irc/ircevent.py @@ -0,0 +1,484 @@ +# gozerbot/ircevent.py +# +# +# http://www.irchelp.org/irchelp/rfc/rfc2812.txt + +""" an ircevent is extracted from the IRC string received from the server. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.log import rlog +from gozerbot.utils.generic import fix_format, toenc, fromenc, stripident +from gozerbot.eventbase import EventBase, makeargrest +from gozerbot.stats import stats +from gozerbot.config import config + +# basic imports +import time, re, types, copy + +cpy = copy.deepcopy + +try: + dotchars = config['dotchars'] + if not dotchars: + dotchars = ' .. ' +except KeyError: + dotchars = ' .. ' + +def makeargrest(ievent): + + """ create ievent.args and ievent.rest .. this is needed because \ + ircevents might be created outside the parse() function. + """ + + try: + ievent.args = ievent.txt.split()[1:] + except ValueError: + ievent.args = [] + + try: + cmnd, ievent.rest = ievent.txt.split(' ', 1) + except ValueError: + ievent.rest = "" + + ievent.command = ievent.txt.split(' ')[0] + +class Ircevent(EventBase): + + """ represents an IRC event. """ + + def __copy__(self): + return Ircevent(self) + + def __deepcopy__(self, bla): + return Ircevent(self) + + def toirc(self): + pass + + def parse(self, bot, rawstr): + + """ parse raw string into ircevent. """ + + self.bot = bot + stats.up('events', bot.name) + bot.nrevents += 1 + rawstr = rawstr.rstrip() + splitted = re.split('\s+', rawstr) + + # check if there is a prefix (: in front) + if not rawstr[0] == ':': + # no prefix .. 1st word is command + splitted.insert(0, ":none!none@none") + rawstr = ":none!none@none " + rawstr + + self.prefix = splitted[0][1:] + + # get nick/userhost + nickuser = self.prefix.split('!') + if len(nickuser) == 2: + self.nick = nickuser[0] + stats.up('events', self.nick) + if self.bot.cfg['stripident'] or config['stripident']: + self.userhost = stripident(nickuser[1]) + else: + self.userhost = nickuser[1] + + # set command + self.cmnd = splitted[1] + self.cbtype = self.cmnd + stats.up('events', self.cmnd) + + # split string based of postfix count .. nr of items ater the command + if pfc.has_key(self.cmnd): + self.arguments = splitted[2:pfc[self.cmnd]+2] + txtsplit = re.split('\s+', rawstr, pfc[self.cmnd]+2) + self.txt = txtsplit[-1] + else: + self.arguments = splitted[2:] + + # 1st argument is target + if self.arguments: + self.target = self.arguments[0] + self.postfix = ' '.join(self.arguments) + + # check if target is text + if self.target and self.target.startswith(':'): + self.txt = ' '.join(self.arguments) + + # strip strarting ':' from txt + if self.txt: + if self.txt[0] == ":": + self.txt = self.txt[1:] + rlog(0, 'ircevent',"%s %s %s" % (self.cmnd, self.arguments, self.txt)) + + # set ircevent attributes + if self.cmnd == 'PING': + self.speed = 10 + if self.cmnd == 'PRIVMSG': + self.channel = self.arguments[0] + if '\001' in self.txt: + self.isctcp = True + elif self.cmnd == 'JOIN' or self.cmnd == 'PART': + if self.arguments: + self.channel = self.arguments[0] + else: + self.channel = self.txt + elif self.cmnd == 'MODE': + self.channel = self.arguments[0] + elif self.cmnd == 'TOPIC': + self.channel = self.arguments[0] + elif self.cmnd == 'KICK': + self.channel = self.arguments[0] + elif self.cmnd == '353': + self.channel = self.arguments[2] + elif self.cmnd == '324': + self.channel = self.arguments[1] + if self.userhost: + # userhost before possible stripident + self.ruserhost = self.userhost + # jabber compat .. this is userhost on irc + self.stripped = self.userhost + # determine user + self.user = stripident(self.userhost).split('@')[0] + + self.origtxt = self.txt + self.channel = self.channel.strip() + stats.up('events', self.channel) + self.origchannel = self.channel + rlog(-1, 'ircevent', self) + + # show error + try: + nr = int(self.cmnd) + if nr > 399: + rlog(10, bot.name + '.error', '%s: %s %s' % (self.cmnd, \ +self.arguments, self.txt)) + except ValueError: + pass + + return self + + def reply(self, txt, result=None, nick=None, dot=False, nritems=False, nr=False, fromm=None, private=False, how=''): + + # don't replu is result is empty list + if result == []: + return + + # stats + stats.up('events', 'replies') + if not how: + try: + how = self.options['--how'] + except KeyError: + how = 'msg' + + # init + restxt = "" + splitted = [] + + # make reply if result is a dict + if type(result) == types.DictType: + for i, j in result.iteritems(): + if type(j) == types.ListType: + try: + z = dotchars.join(j) + except TypeError: + z = unicode(j) + else: + z = j + res = "%s: %s" % (i, z) + splitted.append(res) + if dot == True: + restxt += "%s%s" % (res, dotchars) + else: + restxt += "%s %s" % (dot or ' ', res) + if restxt: + if dot == True: + restxt = restxt[:-6] + elif dot: + restxt = restxt[:-len(dot)] + + lt = False # set if result is list + + # set vars if result is a list + if type(txt) == types.ListType and not result: + result = txt + origtxt = u"" + lt = True + else: + origtxt = txt + + if result: + lt = True + + # if queues are set write output to them + if self.queues: + for i in self.queues: + if splitted: + for item in splitted: + i.put_nowait(item) + elif lt: + for j in result: + i.put_nowait(j) + elif restxt: + i.put_nowait(restxt) + else: + i.put_nowait(txt) + if self.onlyqueues: + return + + # check if bot is set in event + if not self.bot: + rlog(10, 'event', 'no bot defined in event') + return + + # make response + pretxt = origtxt + if lt and not restxt: + res = [] + + # check if there are list in list + for i in result: + if type(i) == types.ListType or type(i) == types.TupleType: + try: + res.append(dotchars.join(i)) + except TypeError: + res.extend(i) + else: + res.append(i) + + # if nritems is set .. + result = res + if nritems: + if len(result) > 1: + pretxt += "(%s items) .. " % len(result) + txtlist = result + + # prepend item number for results + if not nr is False: + try: + start = int(nr) + except ValueError: + start = 0 + txtlist2 = [] + teller = start + for i in txtlist: + txtlist2.append(u"%s) %s" % (teller, i)) + teller += 1 + txtlist = txtlist2 + + # convert results to encoding + txtl = [] + for item in txtlist: + txtl.append(toenc(item)) + txtlist = txtl + + # join result with dot + if dot == True: + restxt = dotchars.join(txtlist) + elif dot: + restxt = dot.join(txtlist) + else: + restxt = ' '.join(txtlist) + + # see if txt needs to be prepended + if pretxt: + try: + restxt = pretxt + restxt + except TypeError: + rlog(10, 'eventbase', "can't add %s and %s" % (str(pretxt), str(restxt))) + + # if txt in result is filtered ignore the reuslt + if self.filtered(restxt): + return + + # if event is DCC based write result directly to socket + if self.cmnd == 'DCC' and self.sock: + self.bot.say(self.sock, restxt, speed=self.speed, how=how) + return + + # if nick is set write result to nick in question + if nick: + self.bot.say(nick, restxt, fromm=nick, speed=self.speed, how=how) + return + + # if originatiog event is a private message or private flaf is set + if self.msg or private: + self.bot.say(self.nick, restxt, fromm=self.nick, speed=self.speed, how=how) + return + + # check if bot is in silent mode .. if so use /msg + silent = False + channel = self.printto or self.channel + try: + silent = self.bot.channels[channel]['silent'] + except (KeyError, TypeError): + pass + fromm = fromm or self.nick + + # check if notice needs to be used + if silent: + notice = False + try: + notice = self.bot.channels[channel]['notice'] + except (KeyError, TypeError): + pass + if notice: + self.bot.say(self.nick, restxt, how='notice', fromm=fromm, speed=self.speed) + else: + self.bot.say(self.nick, restxt, fromm=fromm, speed=self.speed, how=how) + return + + # if printto is set used that as the target + if self.printto: + self.bot.say(self.printto, restxt, fromm=fromm, speed=self.speed, how=how) + return + else: + self.bot.say(self.channel, restxt, fromm=fromm, speed=self.speed, how=how) + + +# postfix count aka how many arguments + +pfc = {} +pfc['NICK'] = 0 +pfc['QUIT'] = 0 +pfc['SQUIT'] = 1 +pfc['JOIN'] = 0 +pfc['PART'] = 1 +pfc['TOPIC'] = 1 +pfc['KICK'] = 2 +pfc['PRIVMSG'] = 1 +pfc['NOTICE'] = 1 +pfc['SQUERY'] = 1 +pfc['PING'] = 0 +pfc['ERROR'] = 0 +pfc['AWAY'] = 0 +pfc['WALLOPS'] = 0 +pfc['INVITE'] = 1 +pfc['001'] = 1 +pfc['002'] = 1 +pfc['003'] = 1 +pfc['004'] = 4 +pfc['005'] = 15 +pfc['302'] = 1 +pfc['303'] = 1 +pfc['301'] = 2 +pfc['305'] = 1 +pfc['306'] = 1 +pfc['311'] = 5 +pfc['312'] = 3 +pfc['313'] = 2 +pfc['317'] = 3 +pfc['318'] = 2 +pfc['319'] = 2 +pfc['314'] = 5 +pfc['369'] = 2 +pfc['322'] = 3 +pfc['323'] = 1 +pfc['325'] = 3 +pfc['324'] = 4 +pfc['331'] = 2 +pfc['332'] = 2 +pfc['341'] = 3 +pfc['342'] = 2 +pfc['346'] = 3 +pfc['347'] = 2 +pfc['348'] = 3 +pfc['349'] = 2 +pfc['351'] = 3 +pfc['352'] = 7 +pfc['315'] = 2 +pfc['353'] = 3 +pfc['366'] = 2 +pfc['364'] = 3 +pfc['365'] = 2 +pfc['367'] = 2 +pfc['368'] = 2 +pfc['371'] = 1 +pfc['374'] = 1 +pfc['375'] = 1 +pfc['372'] = 1 +pfc['376'] = 1 +pfc['381'] = 1 +pfc['382'] = 2 +pfc['383'] = 5 +pfc['391'] = 2 +pfc['392'] = 1 +pfc['393'] = 1 +pfc['394'] = 1 +pfc['395'] = 1 +pfc['262'] = 3 +pfc['242'] = 1 +pfc['235'] = 3 +pfc['250'] = 1 +pfc['251'] = 1 +pfc['252'] = 2 +pfc['253'] = 2 +pfc['254'] = 2 +pfc['255'] = 1 +pfc['256'] = 2 +pfc['257'] = 1 +pfc['258'] = 1 +pfc['259'] = 1 +pfc['263'] = 2 +pfc['265'] = 1 +pfc['266'] = 1 +pfc['401'] = 2 +pfc['402'] = 2 +pfc['403'] = 2 +pfc['404'] = 2 +pfc['405'] = 2 +pfc['406'] = 2 +pfc['407'] = 2 +pfc['408'] = 2 +pfc['409'] = 1 +pfc['411'] = 1 +pfc['412'] = 1 +pfc['413'] = 2 +pfc['414'] = 2 +pfc['415'] = 2 +pfc['421'] = 2 +pfc['422'] = 1 +pfc['423'] = 2 +pfc['424'] = 1 +pfc['431'] = 1 +pfc['432'] = 2 +pfc['433'] = 2 +pfc['436'] = 2 +pfc['437'] = 2 +pfc['441'] = 3 +pfc['442'] = 2 +pfc['443'] = 3 +pfc['444'] = 2 +pfc['445'] = 1 +pfc['446'] = 1 +pfc['451'] = 1 +pfc['461'] = 2 +pfc['462'] = 1 +pfc['463'] = 1 +pfc['464'] = 1 +pfc['465'] = 1 +pfc['467'] = 2 +pfc['471'] = 2 +pfc['472'] = 2 +pfc['473'] = 2 +pfc['474'] = 2 +pfc['475'] = 2 +pfc['476'] = 2 +pfc['477'] = 2 +pfc['478'] = 3 +pfc['481'] = 1 +pfc['482'] = 2 +pfc['483'] = 1 +pfc['484'] = 1 +pfc['485'] = 1 +pfc['491'] = 1 +pfc['501'] = 1 +pfc['502'] = 1 +pfc['700'] = 2 + + +# default event used to initialise events +defaultevent = EventBase() --- gozerbot-0.99.1.orig/build/lib/gozerbot/rest/server.py +++ gozerbot-0.99.1/build/lib/gozerbot/rest/server.py @@ -0,0 +1,265 @@ +# gozerbot/rest/server.py +# +# + +__copyright__ = 'this file is in the public domain' + +from gozerbot.utils.log import rlog +from gozerbot.utils.exception import handle_exception, exceptionmsg +from gozerbot.utils.trace import calledfrom +from gozerbot.config import config +from gozerbot.persist.persiststate import ObjectState +from gozerbot.threads.thr import start_new_thread +from SocketServer import BaseServer, ThreadingMixIn +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from urllib import unquote_plus +from asyncore import dispatcher +from cgi import escape +import time, sys, select, types, socket + +class RestServerBase(HTTPServer): + + """ REST web server """ + + allow_reuse_address = True + daemon_thread = True + + def start(self): + self.name = calledfrom(sys._getframe(0)) + self.stop = False + self.running = False + self.handlers = {} + self.webmods = {} + self.state = ObjectState() + self.state.define('whitelistenable', 0) + self.state.define('whitelist', []) + self.state.define('blacklist', []) + self.state.define('disable', []) + self.poll = select.poll() + self.poll.register(self) + start_new_thread(self.serve, ()) + + def shutdown(self): + try: + self.stop = True + #self.socket.shutdown(2) + #self.socket.close() + time.sleep(0.2) + self.server_close() + except Exception, ex: + handle_exception() + + def serve(self): + rlog(10, self.name, 'starting server') + time.sleep(1) + while not self.stop: + self.running = True + try: + got = self.poll.poll(100) + except Exception, ex: + handle_exception() + if got and not self.stop: + try: + self.handle_request() + except Exception, ex: + handle_exception() + time.sleep(0.01) + self.running = False + rlog(10, self.name, 'stopping server') + + def entrypoint(self, request): + ip = request.ip + if not self.whitelistenable() and ip in self.blacklist(): + rlog(100, self.name, 'denied %s' % ip) + request.send_error(401) + return False + if self.whitelistenable() and ip not in self.whitelist(): + rlog(100, self.name, 'denied %s' % ip) + request.send_error(401) + return False + return True + + def whitelistenable(self): + return self.state['whitelistenable'] + + def whitelist(self): + return self.state['whitelist'] + + def blacklist(self): + return self.state['blacklist'] + + def addhandler(self, path, type, handler): + """ add a web handler """ + splitted = [] + for i in path.split('/'): + if i: + splitted.append(i) + splitted = tuple(splitted) + if not self.handlers.has_key(splitted): + self.handlers[splitted] = {} + self.handlers[splitted][type] = handler + rlog(0, self.name, '%s %s handler added' % (splitted, type)) + + def enable(self, what): + try: + self.state['disable'].remove(what) + rlog(10, self.name, 'enabled %s' % str(what)) + except ValueError: + pass + + def disable(self, what): + self.state['disable'].append(what) + rlog(10, self.name, 'enabled %s' % str(what)) + + def do(self, request): + """ do a request """ + path = request.path.split('?')[0] + if path.endswith('/'): + path = path[:-1] + splitted = [] + for i in path.split('/'): + if i: + splitted.append(i) + splitted = tuple(splitted) + for i in self.state['disable']: + if i in splitted: + rlog(10, self.name, 'denied disabled %s' % i) + request.send_error(404) + return + request.splitted = splitted + request.value = None + type = request.command + try: + func = self.handlers[splitted][type] + except (KeyError, ValueError): + try: + func = self.handlers[splitted[:-1]][type] + request.value = splitted[-1] + except (KeyError, ValueError): + request.send_error(404) + return + result = func(self, request) + rlog(10, self.name, 'result: %s' % str(result)) + return result + + def handle_error(self, request, addr): + """ log the error """ + exctype, excvalue, tb = sys.exc_info() + if exctype == socket.timeout: + rlog(10, self.name, 'socket timeout on %s' % str(addr)) + return + if exctype == socket.error: + rlog(10, self.name, 'socket error on %s: %s' % (str(addr), excvalue)) + return + exceptstr = exceptionmsg() + rlog(10, self.name, 'error on %s: %s %s => %s' % (str(addr), exctype, excvalue, exceptstr)) + + +class RestServer(ThreadingMixIn, RestServerBase): + + pass + +class RestServerAsync(RestServerBase, dispatcher): + + pass + +class RestRequestHandler(BaseHTTPRequestHandler): + + """ timeserver request handler class """ + + def setup(self): + BaseHTTPRequestHandler.setup(self) + self.ip = self.client_address[0] + self.name = self.ip + self.size = 0 + + def writeheader(self, type='text/plain'): + self.send_response(200) + self.send_header('Content-type', '%s; charset=%s ' % (type,sys.getdefaultencoding())) + self.send_header('Server', config['version']) + self.end_headers() + + def sendresult(self): + try: + result = self.server.do(self) + if not result: + return + self.size = len(result) + except Exception, ex: + handle_exception() + self.send_error(501) + return + self.writeheader() + self.wfile.write(result) + self.wfile.close() + + def handle_request(self): + if not self.server.entrypoint(self): + return + self.sendresult() + + do_DELETE = do_PUT = do_GET = do_POST = handle_request + + def log_request(self, code): + """ log the request """ + try: + ua = self.headers['user-agent'] + except: + ua = "-" + try: + rf = self.headers['referer'] + except: + rf = "-" + + if hasattr(self, 'path'): + rlog(10, self.name, '%s "%s %s %s" %s %s "%s" "%s"' % (self.address_string(), self.command, self.path, self.request_version, code, self.size, rf, ua)) + else: + rlog(10, self.name, '%s "%s %s %s" %s %s "%s" "%s"' % (self.address_string(), self.command, "none", self.request_version, code, self.size, rf, ua)) + +class SecureRestServer(RestServer): + + def __init__(self, server_address, HandlerClass, keyfile, certfile): + from OpenSSL import SSL + BaseServer.__init__(self, server_address, HandlerClass) + ctx = SSL.Context(SSL.SSLv23_METHOD) + ctx.set_options(SSL.OP_NO_SSLv2) + rlog(10, self.name, "loading private key from %s" % keyfile) + ctx.use_privatekey_file (keyfile) + rlog(10, self.name, 'loading certificate from %s' % certfile) + ctx.use_certificate_file(certfile) + rlog(10, self.name, 'creating SSL socket on %s' % str(server_address)) + self.socket = SSL.Connection(ctx, socket.socket(self.address_family, + self.socket_type)) + self.server_bind() + self.server_activate() + +class SecureAuthRestServer(SecureRestServer): + + def __init__(self, server_address, HandlerClass, chain, serverkey, servercert): + from OpenSSL import SSL + BaseServer.__init__(self, server_address, HandlerClass) + ctx = SSL.Context(SSL.SSLv23_METHOD) + rlog(10, self.name, "loading private key from %s" % serverkey) + ctx.use_privatekey_file (serverkey) + rlog(10, self.name, 'loading certificate from %s' % servercert) + ctx.use_certificate_file(servercert) + rlog(10, self.name, 'loading chain of certifications from %s' % chain) + ctx.set_verify_depth(2) + ctx.load_client_ca(chain) + #ctx.load_verify_locations(chain) + rlog(10, self.name, 'creating SSL socket on %s' % str(server_address)) + callback = lambda conn,cert,errno,depth,retcode: retcode + ctx.set_verify(SSL.VERIFY_FAIL_IF_NO_PEER_CERT | SSL.VERIFY_PEER, callback) + ctx.set_session_id('gozerbot') + self.socket = SSL.Connection(ctx, socket.socket(self.address_family, + self.socket_type)) + self.server_bind() + self.server_activate() + +class SecureRequestHandler(RestRequestHandler): + + def setup(self): + self.connection = self.request._sock + self.request._sock.setblocking(1) + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.rbufsize) --- gozerbot-0.99.1.orig/build/lib/gozerbot/rest/client.py +++ gozerbot-0.99.1/build/lib/gozerbot/rest/client.py @@ -0,0 +1,285 @@ +# gozerbot/rest/client.py +# +# + +""" Rest Client class """ + +# gozerbot imports +from gozerbot.utils.url import geturl3, geturl4, posturl, deleteurl, useragent +from gozerbot.utils.generic import toenc +from gozerbot.utils.log import rlog +from gozerbot.utils.exception import handle_exception, exceptionmsg +from gozerbot.utils.locking import lockdec +from gozerbot.utils.lazydict import LazyDict + +# simple json import +from simplejson import loads + +# basic imports +from urllib2 import HTTPError, URLError +from httplib import InvalidURL +from urlparse import urlparse +import socket, asynchat, urllib, sys, thread, re, asyncore, time +from errno import EAGAIN, EBADF + +restlock = thread.allocate_lock() +locked = lockdec(restlock) + +class RestResult(LazyDict): + + def __init__(self, url="", name=""): + LazyDict.__init__(self) + self.url = url + self.name = name + self.data = None + self.error = None + self.status = None + self.reason = "" + +class RestClient(object): + + def __init__(self, url, keyfile=None, certfile=None, port=None): + if not url.endswith('/'): + url += '/' + try: + u = urlparse(url) + splitted = u[1].split(':') + if len(splitted) == 2: + host, port = splitted + else: + host = splitted[0] + port = port or 9999 + path = u[2] + except Exception, ex: + raise + self.host = host + try: + self.ip = socket.gethostbyname(self.host) + except Exception, ex: + handle_exception() + self.path = path + self.port = port + self.url = url + self.keyfile = keyfile + self.certfile = certfile + self.callbacks = [] + + def addcb(self, callback): + if not callback: + return + self.callbacks.append(callback) + rlog(0, self.name, 'added callback %s' % str(callback)) + return self + + def delcb(self, callback): + try: + del self.callbacks[callback] + rlog(0, self.name, 'deleted callback %s' % str(callback)) + except ValueError: + pass + + def do(self, func, url, *args, **kwargs): + result = RestResult(url) + try: + rlog(5, url, "calling %s" % str(func)) + res = func(url, {}, kwargs, self.keyfile, self.certfile, self.port) + result.status = res.status + result.reason = res.reason + if result.status >= 400: + result.error = result.status + else: + result.error = None + if result.status == 200: + r = res.read() + result.data = loads(r) + else: + result.data = None + rlog(5, url, "result: %s" % str(result)) + except Exception, ex: + result.error = str(ex) + result.data = None + for cb in self.callbacks: + try: + cb(self, result) + rlog(5, self.name, 'called callback %s' % str(cb)) + except Exception, ex: + handle_exception() + return result + + def post(self, *args, **kwargs): + return self.do(posturl, self.url, *args, **kwargs) + + def add(self, *args, **kwargs): + return self.do(posturl, self.url, *args, **kwargs) + + def delete(self, nr=None): + if nr: + return self.do(deleteurl, self.url + '/' + str(nr)) + else: + return self.do(deleteurl, self.url) + + def get(self, nr=None): + if not nr: + return self.do(geturl4, self.url) + else: + return self.do(geturl4, self.url + '/' + str(nr)) + +class RestClientAsync(RestClient, asynchat.async_chat): + + def __init__(self, url, name=""): + RestClient.__init__(self, url) + asynchat.async_chat.__init__(self) + self.set_terminator("\r\n\r\n") + self.reading_headers = True + self.error = None + self.buffer = '' + self.name = name or self.url + self.headers = {} + self.status = None + + def handle_error(self): + exctype, excvalue, tb = sys.exc_info() + if exctype == socket.error: + try: + errno, errtxt = excvalue + if errno in [EAGAIN, EBADF]: + rlog(10, self.name, "%s %s" % (errno, errtxt)) + return + except ValueError: + pass + self.error = str(excvalue) + else: + rlog(10, self.name, exceptionmsg()) + self.error = exceptionmsg() + self.buffer = '' + result = RestResult(self.url, self.name) + result.error = self.error + result.data = None + for cb in self.callbacks: + try: + cb(self, result) + rlog(0, self.name, 'called callback %s' % str(cb)) + except Exception, ex: + handle_exception() + self.close() + + def handle_expt(self): + handle_exception() + + def handle_connect(self): + rlog(10, self.name, 'connected %s' % str(self)) + + def start(self): + assert(self.host) + assert(int(self.port)) + try: + rlog(5, self.name, 'starting client') + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.connect((self.ip, int(self.port))) + except socket.error, ex: + self.error = str(ex) + try: + self.connect((self.ip, int(self.port))) + except socket.error, ex: + self.error = str(ex) + except Exception, ex: + self.error = str(ex) + if self.error: + rlog(10, self.name, "can't start %s" % self.error) + else: + return True + + @locked + def found_terminator(self): + rlog(10, self.name, 'found terminator') + if self.reading_headers: + self.reading_headers = False + try: + self.headers = self.buffer.split('\r\n') + self.status = int(self.headers[0].split()[1]) + except (ValueError, IndexError): + rlog(10, self.name, "can't parse headers %s" % self.headers) + return + self.set_terminator(None) + self.buffer = '' + rlog(5, self.name, 'headers: %s' % self.headers) + + def collect_incoming_data(self, data): + self.buffer = self.buffer + data + + def handle_close(self): + self.reading_headers = False + self.handle_incoming() + rlog(10, self.name, 'closed') + self.close() + + def handle_incoming(self): + rlog(5, self.name, "incoming: " + self.buffer) + if not self.reading_headers: + result = RestResult(self.url, self.name) + if self.status >= 400: + rlog(10, self.name, 'error status: %s' % self.status) + result.error = self.status + result.data = None + elif self.error: + result.error = self.error + result.data = None + elif self.buffer == "": + result.data = "" + result.error = None + else: + try: + res = loads(self.buffer) + if not res: + self.buffer = '' + return + result.data = res + result.error = None + except ValueError, ex: + rlog(10, self.name, "can't decode %s" % self.buffer) + result.error = str(ex) + except Exception, ex: + rlog(10, self.name, exceptionmsg()) + result.error = exceptionmsg() + result.data = None + for cb in self.callbacks: + try: + cb(self, result) + rlog(0, self.name, 'called callback %s' % str(cb)) + except Exception, ex: + handle_exception() + self.buffer = '' + + @locked + def dorequest(self, method, path, postdata={}, headers={}): + if postdata: + postdata = urllib.urlencode(postdata) + if headers: + if not headers.has_key('Content-Length'): + headers['Content-Length'] = len(postdata) + headerstxt = "" + for i,j in headers.iteritems(): + headerstxt += "%s: %s\r\n" % (i.lower(), j) + else: + headerstxt = "" + if method == 'POST': + s = toenc("%s %s HTTP/1.0\r\n%s\r\n%s\r\n\r\n" % (method, path, headerstxt, postdata), 'ascii') + else: + s = toenc("%s %s HTTP/1.0\r\n\r\n" % (method, path), 'ascii') + if self.start(): + rlog(10, self.url, 'sending %s' % s) + self.push(s) + + def sendpost(self, postdata): + headers = {'Content-Type': 'application/x-www-form-urlencoded', \ +'Accept': 'text/plain; text/html', 'User-Agent': useragent()} + self.dorequest('POST', self.path, postdata, headers) + + def sendget(self): + self.dorequest('GET', self.path) + + def post(self, *args, **kwargs): + self.sendpost(kwargs) + + def get(self): + self.sendget() --- gozerbot-0.99.1.orig/build/lib/gozerbot/contrib/rijndael.py +++ gozerbot-0.99.1/build/lib/gozerbot/contrib/rijndael.py @@ -0,0 +1,376 @@ +""" +A pure python (slow) implementation of rijndael with a decent interface + +To include - + +from rijndael import rijndael + +To do a key setup - + +r = rijndael(key, block_size = 16) + +key must be a string of length 16, 24, or 32 +blocksize must be 16, 24, or 32. Default is 16 + +To use - + +ciphertext = r.encrypt(plaintext) +plaintext = r.decrypt(ciphertext) + +If any strings are of the wrong length a ValueError is thrown +""" + +# ported from the Java reference code by Bram Cohen, April 2001 +# this code is public domain, unless someone makes +# an intellectual property claim against the reference +# code, in which case it can be made public domain by +# deleting all the comments and renaming all the variables + +import copy +import string + +shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]], + [[0, 0], [1, 5], [2, 4], [3, 3]], + [[0, 0], [1, 7], [3, 5], [4, 4]]] + +# [keysize][block_size] +num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}} + +A = [[1, 1, 1, 1, 1, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 1, 1], + [1, 0, 0, 0, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 0, 0, 0, 1]] + +# produce log and alog tables, needed for multiplying in the +# field GF(2^m) (generator = 3) +alog = [1] +for i in xrange(255): + j = (alog[-1] << 1) ^ alog[-1] + if j & 0x100 != 0: + j ^= 0x11B + alog.append(j) + +log = [0] * 256 +for i in xrange(1, 255): + log[alog[i]] = i + +# multiply two elements of GF(2^m) +def mul(a, b): + if a == 0 or b == 0: + return 0 + return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] + +# substitution box based on F^{-1}(x) +box = [[0] * 8 for i in xrange(256)] +box[1][7] = 1 +for i in xrange(2, 256): + j = alog[255 - log[i]] + for t in xrange(8): + box[i][t] = (j >> (7 - t)) & 0x01 + +B = [0, 1, 1, 0, 0, 0, 1, 1] + +# affine transform: box[i] <- B + A*box[i] +cox = [[0] * 8 for i in xrange(256)] +for i in xrange(256): + for t in xrange(8): + cox[i][t] = B[t] + for j in xrange(8): + cox[i][t] ^= A[t][j] * box[i][j] + +# S-boxes and inverse S-boxes +S = [0] * 256 +Si = [0] * 256 +for i in xrange(256): + S[i] = cox[i][0] << 7 + for t in xrange(1, 8): + S[i] ^= cox[i][t] << (7-t) + Si[S[i] & 0xFF] = i + +# T-boxes +G = [[2, 1, 1, 3], + [3, 2, 1, 1], + [1, 3, 2, 1], + [1, 1, 3, 2]] + +AA = [[0] * 8 for i in xrange(4)] + +for i in xrange(4): + for j in xrange(4): + AA[i][j] = G[i][j] + AA[i][i+4] = 1 + +for i in xrange(4): + pivot = AA[i][i] + if pivot == 0: + t = i + 1 + while AA[t][i] == 0 and t < 4: + t += 1 + assert t != 4, 'G matrix must be invertible' + for j in xrange(8): + AA[i][j], AA[t][j] = AA[t][j], AA[i][j] + pivot = AA[i][i] + for j in xrange(8): + if AA[i][j] != 0: + AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255] + for t in xrange(4): + if i != t: + for j in xrange(i+1, 8): + AA[t][j] ^= mul(AA[i][j], AA[t][i]) + AA[t][i] = 0 + +iG = [[0] * 4 for i in xrange(4)] + +for i in xrange(4): + for j in xrange(4): + iG[i][j] = AA[i][j + 4] + +def mul4(a, bs): + if a == 0: + return 0 + r = 0 + for b in bs: + r <<= 8 + if b != 0: + r = r | mul(a, b) + return r + +T1 = [] +T2 = [] +T3 = [] +T4 = [] +T5 = [] +T6 = [] +T7 = [] +T8 = [] +U1 = [] +U2 = [] +U3 = [] +U4 = [] + +for t in xrange(256): + s = S[t] + T1.append(mul4(s, G[0])) + T2.append(mul4(s, G[1])) + T3.append(mul4(s, G[2])) + T4.append(mul4(s, G[3])) + + s = Si[t] + T5.append(mul4(s, iG[0])) + T6.append(mul4(s, iG[1])) + T7.append(mul4(s, iG[2])) + T8.append(mul4(s, iG[3])) + + U1.append(mul4(t, iG[0])) + U2.append(mul4(t, iG[1])) + U3.append(mul4(t, iG[2])) + U4.append(mul4(t, iG[3])) + +# round constants +rcon = [1] +r = 1 +for t in xrange(1, 30): + r = mul(2, r) + rcon.append(r) + +del A +del AA +del pivot +del B +del G +del box +del log +del alog +del i +del j +del r +del s +del t +del mul +del mul4 +del cox +del iG + +class rijndael: + def __init__(self, key, block_size = 16): + if block_size != 16 and block_size != 24 and block_size != 32: + raise ValueError('Invalid block size: ' + str(block_size)) + if len(key) != 16 and len(key) != 24 and len(key) != 32: + raise ValueError('Invalid key size: ' + str(len(key))) + self.block_size = block_size + + ROUNDS = num_rounds[len(key)][block_size] + BC = block_size / 4 + # encryption round keys + Ke = [[0] * BC for i in xrange(ROUNDS + 1)] + # decryption round keys + Kd = [[0] * BC for i in xrange(ROUNDS + 1)] + ROUND_KEY_COUNT = (ROUNDS + 1) * BC + KC = len(key) / 4 + + # copy user material bytes into temporary ints + tk = [] + for i in xrange(0, KC): + tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) | + (ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3])) + + # copy values into round key arrays + t = 0 + j = 0 + while j < KC and t < ROUND_KEY_COUNT: + Ke[t / BC][t % BC] = tk[j] + Kd[ROUNDS - (t / BC)][t % BC] = tk[j] + j += 1 + t += 1 + tt = 0 + rconpointer = 0 + while t < ROUND_KEY_COUNT: + # extrapolate using phi (the round key evolution function) + tt = tk[KC - 1] + tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \ + (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \ + (S[ tt & 0xFF] & 0xFF) << 8 ^ \ + (S[(tt >> 24) & 0xFF] & 0xFF) ^ \ + (rcon[rconpointer] & 0xFF) << 24 + rconpointer += 1 + if KC != 8: + for i in xrange(1, KC): + tk[i] ^= tk[i-1] + else: + for i in xrange(1, KC / 2): + tk[i] ^= tk[i-1] + tt = tk[KC / 2 - 1] + tk[KC / 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \ + (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \ + (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \ + (S[(tt >> 24) & 0xFF] & 0xFF) << 24 + for i in xrange(KC / 2 + 1, KC): + tk[i] ^= tk[i-1] + # copy values into round key arrays + j = 0 + while j < KC and t < ROUND_KEY_COUNT: + Ke[t / BC][t % BC] = tk[j] + Kd[ROUNDS - (t / BC)][t % BC] = tk[j] + j += 1 + t += 1 + # inverse MixColumn where needed + for r in xrange(1, ROUNDS): + for j in xrange(BC): + tt = Kd[r][j] + Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \ + U2[(tt >> 16) & 0xFF] ^ \ + U3[(tt >> 8) & 0xFF] ^ \ + U4[ tt & 0xFF] + self.Ke = Ke + self.Kd = Kd + + def encrypt(self, plaintext): + if len(plaintext) != self.block_size: + raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) + Ke = self.Ke + + BC = self.block_size / 4 + ROUNDS = len(Ke) - 1 + if BC == 4: + SC = 0 + elif BC == 6: + SC = 1 + else: + SC = 2 + s1 = shifts[SC][1][0] + s2 = shifts[SC][2][0] + s3 = shifts[SC][3][0] + a = [0] * BC + # temporary work array + t = [] + # plaintext to ints + key + for i in xrange(BC): + t.append((ord(plaintext[i * 4 ]) << 24 | + ord(plaintext[i * 4 + 1]) << 16 | + ord(plaintext[i * 4 + 2]) << 8 | + ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i]) + # apply round transforms + for r in xrange(1, ROUNDS): + for i in xrange(BC): + a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^ + T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^ + T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^ + T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i] + t = copy.copy(a) + # last round is special + result = [] + for i in xrange(BC): + tt = Ke[ROUNDS][i] + result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) + return string.join(map(chr, result), '') + + def decrypt(self, ciphertext): + if len(ciphertext) != self.block_size: + raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(ciphertext))) + Kd = self.Kd + + BC = self.block_size / 4 + ROUNDS = len(Kd) - 1 + if BC == 4: + SC = 0 + elif BC == 6: + SC = 1 + else: + SC = 2 + s1 = shifts[SC][1][1] + s2 = shifts[SC][2][1] + s3 = shifts[SC][3][1] + a = [0] * BC + # temporary work array + t = [0] * BC + # ciphertext to ints + key + for i in xrange(BC): + t[i] = (ord(ciphertext[i * 4 ]) << 24 | + ord(ciphertext[i * 4 + 1]) << 16 | + ord(ciphertext[i * 4 + 2]) << 8 | + ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i] + # apply round transforms + for r in xrange(1, ROUNDS): + for i in xrange(BC): + a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^ + T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^ + T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^ + T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i] + t = copy.copy(a) + # last round is special + result = [] + for i in xrange(BC): + tt = Kd[ROUNDS][i] + result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) + return string.join(map(chr, result), '') + +def encrypt(key, block): + return rijndael(key, len(block)).encrypt(block) + +def decrypt(key, block): + return rijndael(key, len(block)).decrypt(block) + +def test(): + def t(kl, bl): + b = 'b' * bl + r = rijndael('a' * kl, bl) + assert r.decrypt(r.encrypt(b)) == b + t(16, 16) + t(16, 24) + t(16, 32) + t(24, 16) + t(24, 24) + t(24, 32) + t(32, 16) + t(32, 24) + t(32, 32) --- gozerbot-0.99.1.orig/build/lib/gozerbot/contrib/xmlstream.py +++ gozerbot-0.99.1/build/lib/gozerbot/contrib/xmlstream.py @@ -0,0 +1,271 @@ +## xmlstream.py +## +## Copyright (C) 2001 Matthew Allum +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published +## by the Free Software Foundation; either version 2, or (at your option) +## any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. + + +"""\ +xmlstream.py provides simple functionality for implementing +XML stream based network protocols. It is used as a base +for jabber.py. + +xmlstream.py manages the network connectivity and xml parsing +of the stream. When a complete 'protocol element' ( meaning a +complete child of the xmlstreams root ) is parsed the dipatch +method is called with a 'Node' instance of this structure. +The Node class is a very simple XML DOM like class for +manipulating XML documents or 'protocol elements' in this +case. + +""" + +# $Id: xmlstream.py,v 1.45 2004/02/03 16:33:37 snakeru Exp $ + +import time, sys, re, socket +from base64 import encodestring +import xml.parsers.expat + +__version__ = VERSION = "0.5" + +ENCODING = 'utf-8' # Though it is uncommon, this is the only right setting. +ustr = str + +BLOCK_SIZE = 1024 ## Number of bytes to get at at time via socket + ## transactions + +def XMLescape(txt): + "Escape XML entities" + txt = txt.replace("&", "&") + txt = txt.replace("<", "<") + txt = txt.replace(">", ">") + return txt + +def XMLunescape(txt): + "Unescape XML entities" + txt = txt.replace(">", ">") + txt = txt.replace("<", "<") + txt = txt.replace("&", "&") + return txt + +class error(object): + def __init__(self, value): + self.value = str(value) + def __str__(self): + return self.value + +class Node(object): + """A simple XML DOM like class""" + def __init__(self, tag=None, parent=None, attrs={}, payload=[], node=None): + if node: + if type(node)<>type(self): node=NodeBuilder(node).getDom() + self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = \ + node.name,node.namespace,node.attrs,node.data,node.kids,node.parent + else: + self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = 'tag','',{},[],[],None + + if tag: self.namespace, self.name = (['']+tag.split())[-2:] + + if parent: self.parent = parent + +# if self.parent and not self.namespace: self.namespace=self.parent.namespace # Doesn't checked if this neccessary + + for attr in attrs.keys(): + self.attrs[attr]=attrs[attr] + + for i in payload: + if type(i)==type(self): self.insertNode(i) + else: self.insertXML(i) +# self.insertNode(Node(node=i)) # Alternative way. Needs perfomance testing. + + def setParent(self, node): + "Set the nodes parent node." + self.parent = node + + def getParent(self): + "return the nodes parent node." + return self.parent + + def getName(self): + "Set the nodes tag name." + return self.name + + def setName(self,val): + "Set the nodes tag name." + self.name = val + + def putAttr(self, key, val): + "Add a name/value attribute to the node." + self.attrs[key] = val + + def getAttr(self, key): + "Get a value for the nodes named attribute." + try: return self.attrs[key] + except: return None + + def getAttributes(self): + "Get a value for the nodes named attribute." + return self.attrs + + def putData(self, data): + "Set the nodes textual data" + self.data.append(data) + + def insertData(self, data): + "Set the nodes textual data" + self.data.append(data) + + def getData(self): + "Return the nodes textual data" + return ''.join(self.data) + + def getDataAsParts(self): + "Return the node data as an array" + return self.data + + def getNamespace(self): + "Returns the nodes namespace." + return self.namespace + + def setNamespace(self, namespace): + "Set the nodes namespace." + self.namespace = namespace + + def insertTag(self, name=None, attrs={}, payload=[], node=None): + """ Add a child tag of name 'name' to the node. + + Returns the newly created node. + """ + newnode = Node(tag=name, parent=self, attrs=attrs, payload=payload, node=node) + self.kids.append(newnode) + return newnode + + def insertNode(self, node): + "Add a child node to the node" + self.kids.append(node) + return node + + def insertXML(self, xml_str): + "Add raw xml as a child of the node" + newnode = NodeBuilder(xml_str).getDom() + self.kids.append(newnode) + return newnode + + def __str__(self): + return self._xmlnode2str() + + def _xmlnode2str(self, parent=None): + """Returns an xml ( string ) representation of the node + and it children""" + s = "<" + self.name + if self.namespace: + if parent and parent.namespace != self.namespace: + s = s + " xmlns = '%s' " % self.namespace + for key in self.attrs.keys(): + val = ustr(self.attrs[key]) + s = s + " %s='%s'" % ( key, XMLescape(val) ) + s = s + ">" + cnt = 0 + if self.kids != None: + for a in self.kids: + if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) + s = s + a._xmlnode2str(parent=self) + cnt=cnt+1 + if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) + if not self.kids and s[-1:]=='>': + s=s[:-1]+' />' + else: + s = s + "" + return s + + def getTag(self, name, index=None): + """Returns a child node with tag name. Returns None + if not found.""" + for node in self.kids: + if node.getName() == name: + if not index: return node + if index is not None: index-=1 + return None + + def getTags(self, name): + """Like getTag but returns a list with matching child nodes""" + nodes=[] + for node in self.kids: + if node.getName() == name: + nodes.append(node) + return nodes + + def getChildren(self): + """Returns a nodes children""" + return self.kids + + def removeTag(self,tag): + """Pops out specified child and returns it.""" + if type(tag)==type(self): + try: + self.kids.remove(tag) + return tag + except: return None + for node in self.kids: + if node.getName()==tag: + self.kids.remove(node) + return node + +class NodeBuilder(object): + """builds a 'minidom' from data parsed to it. Primarily for insertXML + method of Node""" + def __init__(self,data=None): + self._parser = xml.parsers.expat.ParserCreate() + self._parser.StartElementHandler = self.unknown_starttag + self._parser.EndElementHandler = self.unknown_endtag + self._parser.CharacterDataHandler = self.handle_data + self.__depth = 0 + self._dispatch_depth = 1 + self.last_is_data = False + self._ptr = Node() + if data: self._parser.Parse(data,1) + + def unknown_starttag(self, tag, attrs): + """XML Parser callback""" + self.__depth = self.__depth + 1 + if self.__depth == self._dispatch_depth: + self._mini_dom = Node(tag=tag, attrs=attrs) + self._ptr = self._mini_dom + elif self.__depth > self._dispatch_depth: + self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs)) + self._ptr = self._ptr.kids[-1] + else: ## it the stream tag: + if attrs.has_key('id'): + self._incomingID = attrs['id'] + self.last_is_data = False + + def unknown_endtag(self, tag ): + """XML Parser callback""" + if self.__depth == self._dispatch_depth: + self.dispatch(self._mini_dom) + elif self.__depth > self._dispatch_depth: + self._ptr = self._ptr.parent + self.__depth = self.__depth - 1 + self.last_is_data = False + + def handle_data(self, data): + """XML Parser callback""" + if self.last_is_data: + self._ptr.data[-1] += data + else: + self._ptr.data.append(data) + self.last_is_data = True + + def dispatch(self,dom): + pass + + def getDom(self): + return self._mini_dom --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/code.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/code.py @@ -0,0 +1,67 @@ +# plugs/code.py +# +# + +""" code related commands. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.redispatcher import rebefore, reafter +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.utils.exception import exceptionlist +from gozerbot.plugins import plugins +from gozerbot.aliases import aliasset +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +plughelp.add('code', 'the code plugin provides code related commands') + +def handle_showexceptions(bot, ievent): + + """ show exception list. """ + + try: + printto = ievent.args[0] + except IndexError: + printto = ievent.channel + cp = list(exceptionlist) + for exception in cp: + bot.say(printto, exception, groupchat=False) + if not exceptionlist: + ievent.reply('no exceptions yet !') + +cmnds.add('code-exceptions' , handle_showexceptions, 'OPER') +examples.add('code-exceptions', 'show exception list', '1) code-exceptions 2) code-exceptions bthate@gmail.com') +aliasset('exceptions', 'code-exceptions') +tests.add('code-exceptions') + +def handle_funcnames(bot, ievent): + + """ show function names of a plugin. """ + + try: + plugname = ievent.args[0] + except IndexError: + ievent.missing('') + return + + if not plugins.exist(plugname): + ievent.reply('no %s plugin exists' % plugname) + return + + funcnames = [] + funcnames = rebefore.getfuncnames(plugname) + funcnames += cmnds.getfuncnames(plugname) + funcnames += reafter.getfuncnames(plugname) + + if funcnames: + ievent.reply(funcnames, dot=True) + else: + ievent.reply("can't find funcnames for %s plugin" % plugname) + +cmnds.add('code-funcnames', handle_funcnames, 'OPER') +examples.add('code-funcnames', 'show function names of a plugin', 'code-funcnames birthday') +aliasset('funcnames', 'code-funcnames') +tests.add('code-funcnames core', 'handle_version') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/tail.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/tail.py @@ -0,0 +1,42 @@ +# plugs/tail.py +# +# + +""" tail bot results. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.generic import waitforqueue +from gozerbot.commands import cmnds +from gozerbot.plughelp import plughelp +from gozerbot.examples import examples +from gozerbot.tests import tests + +plughelp.add('tail', 'show last elements of pipeline') + +def handle_tail(bot, ievent): + + """ used in a pipeline .. show last elements. """ + + if not ievent.inqueue: + ievent.reply("use tail in a pipeline") + return + + try: + nr = int(ievent.args[0]) + except (ValueError, IndexError): + ievent.reply('tail ') + return + + result = waitforqueue(ievent.inqueue, 30) + + if not result: + ievent.reply('no data to tail') + return + + ievent.reply(result[-nr:]) + +cmnds.add('tail', handle_tail, ['USER', 'CLOUD'], threaded=True) +examples.add('tail', 'show last lines of pipeline output', 'list | tail 5') +tests.add('list | tail 5') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/throttle.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/throttle.py @@ -0,0 +1,142 @@ +# gozerbot/plugs/throttle.py +# +# + +from gozerbot.persist.persiststate import PlugState +from gozerbot.persist.persistconfig import PersistConfig +from gozerbot.callbacks import callbacks +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.users import users +from gozerbot.generic import getwho, rlog +from gozerbot.tests import tests + +import time + +plughelp.add('throttle', 'throttle user commands per minute') + +state = PlugState() +state.define('lasttime', {}) +state.define('level', {}) +state.define('cpm', {}) # commands per minute + +cfg = PersistConfig() +cfg.define('enable', 1) + +def throttlepre(bot, ievent): + if cfg.get('enable'): + return 1 + +def throttlecb(bot, ievent): + try: + cpm = state['cpm'] + uh = ievent.userhost + if ievent.usercmnd: + if cpm.has_key(uh): + cpm[uh] += 1 + else: + cpm[uh] = 1 + if uh in bot.throttle: + if time.time() - state['lasttime'][uh] > 60: + bot.throttle.remove(uh) + cpm[uh] = 0 + ievent.reply('next command is unthrottled (%s)' % uh) + rlog(10, 'throttle', 'unignoring %s' % uh) + else: + if ievent.usercmnd and (cpm[uh] > state['level'][uh]): + perms = users.getperms(uh) + if perms and 'OPER' in perms: + cpm[uh] = 0 + return + bot.throttle.append(uh) + ievent.reply('%s throttled' % uh) + rlog(10, 'throttle', 'ignoring %s' % uh) + state['lasttime'][uh] = time.time() + except (ValueError, KeyError): + pass + +def init(): + callbacks.add('PRIVMSG', throttlecb, throttlepre) + +def handle_throttleget(bot, ievent): + if not ievent.rest: + ievent.missing("") + return + nick = ievent.rest + uh = getwho(bot, nick) + if not uh: + ievent.reply("can't find userhost of %s" % nick) + return + try: + ievent.reply("cpm of %s is %s" % (uh, state['level'][uh])) + except KeyError: + pass + +cmnds.add('throttle-get', handle_throttleget, 'OPER') +examples.add('throttle-get', 'get commands per minute of ', \ +'throttle-get dunker') +tests.add('throttle-get exec') + +def handle_throttleset(bot, ievent): + try: + (nick, cpm) = ievent.args + except ValueError: + ievent.missing(' ') + return + uh = getwho(bot, nick) + if not uh: + ievent.reply("can't find userhost of %s" % nick) + return + perms = users.getperms(uh) + if perms and 'OPER' in perms: + ievent.reply("can't throttle a OPER") + return + try: + cpm = float(cpm) + if cpm == 0: + ievent.reply("cpm can't be zero") + return + state['level'][uh] = cpm + state['cpm'][uh] = 0 + state.save() + except ValueError: + ievent.reply('%s is not an integer' % cpm) + return + try: + bot.throttle.remove(uh) + except ValueError: + pass + ievent.reply('cpm set to %s for %s' % (cpm, uh)) + +cmnds.add('throttle-set', handle_throttleset, 'OPER') +examples.add('throttle-set', 'set allowed commands per minute for \ +' , 'throttle-set dunker 10') +tests.add('throttle-set mekker 10') + +def handle_throttleremove(bot, ievent): + if not ievent.rest: + ievent.missing('') + return + uh = getwho(bot, ievent.rest) + if not uh: + ievent.reply("can't find userhost of %s" % ievent.rest) + return + try: + bot.throttle.remove(uh) + state['cpm'][uh] = 0 + state.save() + ievent.reply('throttle on %s removed' % uh) + except (KeyError, ValueError): + pass + +cmnds.add('throttle-remove', handle_throttleremove, 'OPER') +examples.add('throttle-remove', 'remove throttle from ', \ +'throttle-remove dunker') +tests.add('throttle-remove exec') + +def handle_throttlelist(bot, ievent): + ievent.reply(bot.throttle) + +cmnds.add('throttle-list', handle_throttlelist, 'OPER') +tests.add('throttle-list') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/chanperm.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/chanperm.py @@ -0,0 +1,108 @@ +# plugs/chanperm.py +# +# + +""" + allow all user in a channel to have permissions. permission that use + userstate information can not be used, for that the users must be meeted to + the bot. +""" + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.utils.log import rlog +from gozerbot.tests import tests +from gozerbot.utils.locking import lockdec + +import thread + +plughelp.add('chanperm', 'manage channel permissions') + +chanpermlock = thread.allocate_lock() +chanpermlocked = lockdec(chanpermlock) + +@chanpermlocked +def handle_chanperm(bot, ievent): + + """ show channel permissions. """ + + chan = ievent.channel.lower() + + try: + p = bot.channels[chan]['perms'] + except (KeyError, TypeError): + ievent.reply("channel %s has no permissions set" % chan) + return + + if p: + ievent.reply('permissions of channel %s: ' % chan, p, dot=True) + else: + ievent.reply("channel %s has no permissions set" % chan) + +cmnds.add('chanperm', handle_chanperm, 'OPER') +examples.add('chanperm', 'show channel permissions', 'chanperm') +tests.add('chanperm-add mekker --chan #dunkbots').add('chanperm chan #dunkbots', 'MEKKER').add('chanperm-del chan #dunkbots mekker') + +@chanpermlocked +def handle_chanpermadd(bot, ievent): + + """ add channel permission. """ + + try: + perm = ievent.args[0].upper() + except IndexError: + ievent.missing('') + return + + if perm in ['OPER', 'USER']: + ievent.reply("can't set channel permission to %s" % perm) + return + + chan = ievent.channel.lower() + + try: + if perm in bot.channels[chan]['perms']: + ievent.reply('channel %s already has %s permission set' % \ +(chan, perm)) + return + except (KeyError, TypeError): + try: + bot.channels[chan].setdefault('perms', []) + except AttributeError: + ievent.reply('channel %s is not in channel database' % chan) + return + + bot.channels[chan]['perms'].append(perm.upper()) + bot.channels.save() + ievent.reply('%s channel perm added' % perm) + +cmnds.add('chanperm-add', handle_chanpermadd, 'OPER', allowqueue=False) +examples.add('chanperm-add', 'add channel permission ', 'chanperm-add ANONKARMA') +tests.add('chanperm-add mekker chan #dunkbots', 'MEKKER').add('chanperm-del mekker chan #dunkbots') + +@chanpermlocked +def handle_chanpermdel(bot, ievent): + + """ delete channel permission. """ + + try: + perm = ievent.args[0] + except IndexError: + ievent.missing('') + return + + chan = ievent.channel.lower() + + try: + bot.channels[chan]['perms'].remove(perm.upper()) + ievent.reply('%s channel perm deleted' % perm) + except (ValueError, KeyError, TypeError): + ievent.reply('there is no %s permission for channel %s' % (perm, chan)) + +cmnds.add('chanperm-del', handle_chanpermdel, 'OPER') +examples.add('chanperm-del', 'delete channel permission ', 'chanperm-del ANONKARMA') +tests.add('chanperm-add mekker chan #dunkbots').add('chanperm-del mekker chan #dunkbots', 'mekker') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/size.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/size.py @@ -0,0 +1,47 @@ +# gozerbot/plugs/size.py +# +# + +""" show sizes of plugin data. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.users import users +from gozerbot.redispatcher import rebefore, reafter +from gozerbot.aliases import aliases +from gozerbot.callbacks import callbacks +from gozerbot.plugins import plugins +from gozerbot.fleet import fleet +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +plughelp.add('size', 'the size command shows the sizes of plugins that \ +provide a size() plugin command and the sizes of some basic structures') + +def handle_size(bot, ievent): + + """ size .. show size of core datastructures and plugins that provide a size() function. """ + + txtlist = [] + txtlist.append("fleet: %s" % fleet.size()) + txtlist.append("users: %s" % users.size()) + txtlist.append("cmnds: %s" % cmnds.size()) + txtlist.append("callbacks: %s" % callbacks.size()) + txtlist.append("rebefore: %s" % rebefore.size()) + txtlist.append("reafter: %s" % reafter.size()) + txtlist.append("aliases: %s" % len(aliases.data)) + txtlist.append("examples: %s" % examples.size()) + + plugsizes = plugins.plugsizes() + + if plugsizes: + txtlist += plugsizes + + ievent.reply(txtlist) + +cmnds.add('size', handle_size, ['USER', 'WEB']) +examples.add('size', 'show sizes of various data structures', 'size') +tests.add('size', 'users') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/mysqlkeepalive.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/mysqlkeepalive.py @@ -0,0 +1,27 @@ +# gozerplugs/mysqlkeepalive +# +# + +from gozerbot.users import users +from gozerbot.utils.log import rlog +from gozerbot.threads.threadloop import ThreadSleeper +from gozerbot.config import config + +import time + +class PingLoop(ThreadSleeper): + + + def handle(self): + + if config['dbtype'] == 'mysql': + users.size() + rlog(10, 'mysqlkeepalive', 'pinged database') + +pingloop = PingLoop(timeout=1800) + +def init(): + pingloop.start() + +def shutdown(): + pingloop.stop() --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/user.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/user.py @@ -0,0 +1,945 @@ +# dbplugs/user.py +# +# + +""" users related commands """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.generic import getwho, stripident +from gozerbot.utils.exception import handle_exception +from gozerbot.utils.log import rlog +from gozerbot.utils.name import stripname +from gozerbot.users import users +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.aliases import aliasdel, aliasset +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +plughelp.add('user', 'manage users') + +def handle_whoami(bot, ievent): + + """ user-whoami .. get your username. """ + + ievent.reply('%s' % users.getname(ievent.userhost)) + +cmnds.add('user-whoami', handle_whoami, 'USER') +examples.add('user-whoami', 'get your username', 'user-whoami') +aliasset('whoami', 'user-whoami') +tests.add('whoami') + +def handle_meet(bot, ievent): + + """ user-meet .. introduce a new user to the bot. """ + + try: + nick = ievent.args[0] + except IndexError: + ievent.missing('') + return + + if users.exist(nick): + ievent.reply('there is already a user with username %s' % nick) + return + + userhost = getwho(bot, nick) + + if not userhost: + ievent.reply("can't find userhost of %s" % nick) + return + + username = users.getname(userhost) + + if username: + ievent.reply('we already have a user with userhost %s (%s)' % \ +(userhost, username)) + return + + result = 0 + name = stripname(nick.lower()) + result = users.add(name, [userhost, ], ['USER', ]) + + if result: + ievent.reply('%s (%s) added to user database' % (nick, name)) + else: + ievent.reply('add failed') + +cmnds.add('user-meet', handle_meet, ['OPER', 'MEET']) +examples.add('user-meet', 'user-meet .. introduce to the \ +bot', 'user-meet dunker') +aliasset('meet', 'user-meet') +tests.add('meet test').add('delete test') + +def handle_adduser(bot, ievent): + + """ user-add .. introduce a new user to the bot. """ + + try: + (name, userhost) = ievent.args + except ValueError: + ievent.missing(' ') + return + + username = users.getname(userhost) + + if username: + ievent.reply('we already have a user with userhost %s (%s)' % \ +(userhost, username)) + return + + result = 0 + name = stripname(name.lower()) + result = users.add(name, [userhost, ], ['USER', ]) + + if result: + ievent.reply('%s added to user database' % name) + else: + ievent.reply('add failed') + +cmnds.add('user-add', handle_adduser, 'OPER') +examples.add('user-add', 'user-add .. add with \ + to the bot', 'user-add dunker bart@localhost') +tests.add('user-add mtest1 mekker@test1', 'mtest1 added to user database').add('delete mtest1') + +def handle_merge(bot, ievent): + + """ user-merge .. merge the userhost into a already existing user. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, nick = ievent.args + name = name.lower() + + if users.gotperm(name, 'OPER') and not users.allowed(ievent.userhost, \ +'OPER'): + ievent.reply("only OPER perm can merge with OPER user") + return + + if name == 'owner' and not bot.ownercheck(ievent, "can merge with owner \ +user"): + return + + if not users.exist(name): + ievent.reply("we have no user %s" % name) + return + + userhost = getwho(bot, nick) + if not userhost: + ievent.reply("can't find userhost of %s" % nick) + return + + username = users.getname(userhost) + if username: + ievent.reply('we already have a user with userhost %s (%s)' % \ +(userhost, username)) + return + + result = users.merge(name, userhost) + + if result: + ievent.reply('%s merged' % nick) + else: + ievent.reply('merge failed') + +cmnds.add('user-merge', handle_merge, ['OPER', 'MEET']) +examples.add('user-merge', 'user-merge .. merge record with \ + with userhost from ', 'merge bart dunker') +aliasset('merge', 'user-merge') +tests.add('user-add mtest2 mekker@test2').add('merge mtest2 bottest', 'bottest merged').add('delete mtest2') + +def handle_delete(bot, ievent): + + """ user-del .. remove user. """ + + if len(ievent.args) == 0: + ievent.missing('') + return + + name = ievent.args[0].lower() + + if name == 'owner': + ievent.reply("can't delete owner") + return + + result = 0 + #name = stripname(name.lower()) + name = name.lower() + result = users.delete(name) + + if result: + ievent.reply('%s deleted' % name) + else: + ievent.reply('delete of %s failed' % name) + +cmnds.add('user-del', handle_delete, 'OPER') +examples.add('user-del', 'user-del .. delete user with ' , 'user-del dunker') +aliasset('delete', 'user-del') +tests.add('user-add mtest3 mekker@test3').add('delete mtest3', 'mtest3 deleted') + +def handle_userscan(bot, ievent): + + """ user-scan .. scan for user. """ + + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + + name = name.lower() + names = users.names() + + result = [] + for i in names: + if i.find(name) != -1: + result.append(i) + + if result: + ievent.reply("users matching %s: " % name, result, dot=True) + else: + ievent.reply('no users matched') + return + +cmnds.add('user-scan', handle_userscan, 'OPER') +examples.add('user-scan', 'user-scan .. search database for matching usernames', 'user-scan dunk') +aliasset('us', 'user-scan') +tests.add('user-add mtest4 mekker@test4').add('user-scan mte', 'mtest4').add('delete mtest4') + +def handle_names(bot, ievent): + + """ user-names .. show registered users. """ + + ievent.reply("usernames: ", users.names(), dot=True) + +cmnds.add('user-names', handle_names, 'OPER') +examples.add('user-names', 'show names of registered users', 'user-names') +aliasset('names', 'user-names') +tests.add('user-add mtest5 mekker@test').add('user-names', 'mtest5').add('delete mtest5') + +def handle_name(bot, ievent): + + """ user-name .. show name of user giving the command. """ + + ievent.reply('your name is %s' % users.getname(ievent.userhost)) + +cmnds.add('user-name', handle_name, 'USER') +examples.add('user-name', 'show name of user giving the commands', 'user-name') +aliasset('name', 'user-name') +tests.add('user-name', 'exec') + +def handle_getname(bot, ievent): + + """ user-getname .. fetch name of nick. """ + + try: + nick = ievent.args[0] + except IndexError: + ievent.missing("") + return + + userhost = getwho(bot, nick) + if not userhost: + ievent.reply("can't find userhost of %s" % nick) + return + + name = users.getname(userhost) + if not name: + ievent.reply("can't find user for %s" % userhost) + return + + ievent.reply(name) + +cmnds.add('user-getname', handle_getname, 'USER') +examples.add('user-getname', 'user-getname .. get the name of ', 'user-getname dunker') +aliasset('gn', 'user-getname') +aliasset('getname', 'user-getname') +tests.add('user-getname test') + +def handle_addperm(bot, ievent): + + """ user-addperm .. add permission. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, perm = ievent.args + perm = perm.upper() + name = name.lower() + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + result = 0 + + if users.gotperm(name, perm): + ievent.reply('%s already has permission %s' % (name, perm)) + return + + result = users.addperm(name, perm) + + if result: + ievent.reply('%s perm added' % perm) + else: + ievent.reply('perm add failed') + +cmnds.add('user-addperm', handle_addperm, 'OPER') +examples.add('user-addperm', 'user-addperm .. add permissions to user ', 'user-addperm dunker rss') +aliasset('setperms', 'user-addperm') +aliasset('addperms', 'user-addperm') +tests.add('user-add mtest6 mekker@test6', 'mtest6').add('user-addperm mtest6 mekker', 'MEKKER perm added').add('delete mtest6') + +def handle_getperms(bot, ievent): + + """ user-getperms .. get permissions of name. """ + + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + + name = name.lower() + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + perms = users.getuserperms(name) + if perms: + ievent.reply("permissions of %s: " % name, perms, dot=True) + else: + ievent.reply('%s has no permissions set' % name) + +cmnds.add('user-getperms', handle_getperms, 'OPER') +examples.add('user-getperms', 'user-getperms .. get permissions of ', 'user-getperms dunker') +aliasset('getperms', 'user-getperms') +tests.add('user-add mtest7 mekker@test7').add('user-addperm mtest7 mekker').add('user-getperms mtest7' , 'MEKKER').add('delete mtest7') + +def handle_perms(bot, ievent): + + """ user-perms .. get permission of the user given the command. """ + + if ievent.rest: + ievent.reply("use getperms to get the permissions of somebody else") + return + + name = users.getname(ievent.userhost) + if not name: + ievent.reply("can't find username for %s" % ievent.userhost) + return + + perms = users.getuserperms(name) + if perms: + ievent.reply("you have permissions: ", perms, dot=True) + +cmnds.add('user-perms', handle_perms, 'USER') +examples.add('user-perms', 'get permissions', 'user-perms') +aliasset('perms', 'user-perms') +tests.add('user-perms', 'USER') + +def handle_delperm(bot, ievent): + + """ user-delperm .. delete permission of name. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, perm = ievent.args + perm = perm.upper() + name = name.lower() + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + result = users.deluserperm(name, perm) + + if result: + ievent.reply('%s perm removed' % perm) + else: + ievent.reply("%s has no %s permission" % (name, perm)) + return + +cmnds.add('user-delperm', handle_delperm, 'OPER') +examples.add('user-delperms', 'delete from user permission ', 'user-delperms dunker rss') +tests.add('user-add mtest8 mekker@test8').add('user-addperm mtest8mekker').add('user-delperm mtest8 mekker', 'MEKKER').add('delete mtest8') + +def handle_addstatus(bot, ievent): + + """ user-addstatus .. add status of name. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, status = ievent.args + status = status.upper() + name = name.lower() + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + if users.gotstatus(name, status): + ievent.reply('%s already has status %s' % (name, status)) + return + + result = users.addstatus(name, status) + + if result: + ievent.reply('%s status added' % status) + else: + ievent.reply('add failed') + +cmnds.add('user-addstatus', handle_addstatus, 'OPER') +examples.add('user-addstatus', 'user-addstatus ', 'user-addstatus dunker #dunkbots') +aliasset('setstatus', 'user-addstatus') +aliasset('addstatus', 'user-addstatus') +tests.add('user-add mtest9 mekker@test9').add('user-addstatus mtest9 mekker', 'MEKKER status added').add('delete mtest9') + +def handle_getstatus(bot, ievent): + + """ user-getstatus .. get status of name. """ + + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + + name = name.lower() + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + status = users.getuserstatuses(name) + if status: + ievent.reply("status of %s: " % name, status, dot=True) + else: + ievent.reply('%s has no status set' % name) + +cmnds.add('user-getstatus', handle_getstatus, 'OPER') +examples.add('user-getstatus', 'user-getstatus .. get status of ', 'user-getstatus dunker') +aliasset('getstatus', 'user-getstatus') +tests.add('user-add mtest10 mekker@test10').add('user-addstatus mtest10 mekker', 'MEKKER status added').add('user-getstatus mtest10', 'MEKKER').add('delete mtest10') + +def handle_status(bot, ievent): + + """ user-status .. get status of user given the command. """ + status = users.getstatuses(ievent.userhost) + if status: + ievent.reply("you have status: ", status, dot=True) + else: + ievent.reply('you have no status set') + +cmnds.add('user-status', handle_status, 'USER') +examples.add('user-status', 'get status', 'user-status') +aliasset('status', 'user-status') +tests.add('user-status') + +def handle_delstatus(bot, ievent): + + """ user-delstatus .. delete status of name. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, status = ievent.args + status = status.upper() + name = name.lower() + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + result = users.deluserstatus(name, status) + + if result: + ievent.reply('%s status deleted' % status) + else: + ievent.reply("%s has no %s status" % (name, status)) + return + +cmnds.add('user-delstatus', handle_delstatus, 'OPER') +examples.add('user-delstatus', 'user-delstatus ', 'user-delstatus dunker #dunkbots') +aliasset('delstatus', 'user-delstatus') +tests.add('user-add mtest11 mekker@test11').add('user-addstatus mtest11 mekker').add('user-delstatus mtest11 mekker', 'MEKKER status deleted').add('delete mtest11') + +def handle_adduserhost(bot, ievent): + + """ user-adduserhost .. add to userhosts of name. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, userhost = ievent.args + name = name.lower() + if name == 'owner' and not bot.ownercheck(ievent, 'can adduserhost to \ +owner'): + return + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + if users.gotuserhost(name, userhost): + ievent.reply('%s already has userhost %s' % (name, userhost)) + return + + result = users.adduserhost(name, userhost) + + if result: + ievent.reply('userhost added') + else: + ievent.reply('add failed') + +cmnds.add('user-adduserhost', handle_adduserhost, 'OPER') +examples.add('user-adduserhost', 'user-adduserhost ', 'user-adduserhost dunker bart@%.a2000.nl') +aliasset('adduserhost', 'user-adduserhost') +aliasset('adduserhosts', 'user-adduserhost') +tests.add('user-add mtest12 mekker@test12').add('user-adduserhost mtest12 mekker2@test122', 'userhost added').add('delete mtest12') + +def handle_deluserhost(bot, ievent): + + """ user-deluserhost .. remove from userhosts of name. """ + + if len(ievent.args) != 2: + ievent.missing(' ') + return + + name, userhost = ievent.args + name = name.lower() + if name == 'owner' and not bot.ownercheck(ievent, 'can delete userhosts \ +from owner'): + return + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + result = users.deluserhost(name, userhost) + + if result: + ievent.reply('userhost removed') + else: + ievent.reply("%s has no %s in userhost list" % (name, \ +userhost)) + return + +cmnds.add('user-deluserhost', handle_deluserhost, 'OPER') +examples.add('user-deluserhost', 'user-deluserhost .. delete from usershosts of userhost ','user-deluserhost dunker bart1@bla.a2000.nl') +aliasset('deluserhost', 'user-deluserhost') +aliasset('deluserhosts', 'user-deluserhost') +tests.add('user-add mtest32 mekker@test32').add('user-adduserhost mtest12 mekker2@test32').add('user-deluserhost mtest12 mekker2@test32', 'userhost removed').add('delete mtest32') + +def handle_getuserhosts(bot, ievent): + + """ user-getuserhosts .. get userhosts of name. """ + + try: + who = ievent.args[0] + except IndexError: + ievent.missing('') + return + + who = who.lower() + userhosts = users.getuserhosts(who) + if userhosts: + ievent.reply("userhosts of %s: " % who, userhosts, dot=True) + else: + ievent.reply("can't find user %s" % who) + +cmnds.add('user-getuserhosts', handle_getuserhosts, 'OPER') +examples.add('user-getuserhosts', 'user-getuserhosts .. get userhosts of ', 'getuserhosts dunker') +aliasset('getuserhosts', 'user-getuserhosts') +tests.add('user-add mtest13 mekker@test13').add('user-adduserhost mtest13 mekker@test13').add('user-getuserhosts mtest13', 'mekker@test13').add('delete mtest13') + +def handle_userhosts(bot, ievent): + + """ user-userhosts .. get userhosts of user giving the command. """ + + userhosts = users.gethosts(ievent.userhost) + if userhosts: + ievent.reply("you have userhosts: ", userhosts, dot=True) + +cmnds.add('user-userhosts', handle_userhosts, 'USER') +examples.add('user-userhosts', 'get userhosts', 'user-userhosts') +aliasset('userhosts', 'user-userhosts') +tests.add('user-userhosts') + +def handle_getemail(bot, ievent): + + """ user-getemail .. get email of name. """ + + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + + name = name.lower() + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + email = users.getuseremail(name) + if email: + ievent.reply(email) + else: + ievent.reply('no email set') + +cmnds.add('user-getemail', handle_getemail, 'USER') +examples.add('user-getemail', 'user-getemail .. get email from user ', 'user-getemail dunker') +aliasset('getemail', 'user-getemail') +tests.add('user-add mtest14 mekker@test14').add('user-setemail mtest14 mekker@test14').add('user-getemail mtest14', 'mekker@test14').add('delete mtest14') + +def handle_setemail(bot, ievent): + + """ user-setemail .. set email of name. """ + + try: + name, email = ievent.args + except ValueError: + ievent.missing(' ') + return + + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + users.setemail(name, email) + ievent.reply('email set') + +cmnds.add('user-setemail', handle_setemail, 'OPER') +examples.add('user-setemail', 'user-setemail .. set email of user ', 'user-setemail dunker bart@gozerbot.org') +aliasset('setemail', 'user-setemail') +tests.add('user-add mtest15 mekker@test15').add('user-setemail mtest15 mekker@test15', 'email set').add('delete mtest15') + +def handle_email(bot, ievent): + + """ user-email .. show email of user giving the command. """ + + if len(ievent.args) != 0: + ievent.reply('use getemail to get the email address of an user .. \ +email shows your own mail address') + return + + email = users.getemail(ievent.userhost) + if email: + ievent.reply(email) + else: + ievent.reply('no email set') + +cmnds.add('user-email', handle_email, 'USER') +examples.add('user-email', 'get email', 'user-email') +aliasset('email', 'user-email') +tests.add('user-setemail mekker@mekker').add('user-email', 'mekker@mekker') + +def handle_delemail(bot, ievent): + + """ user-delemail .. reset email of user giving the command. """ + + name = users.getname(ievent.userhost) + if not name: + ievent.reply("can't find user for %s" % ievent.userhost) + return + + result = users.delallemail(name) + + if result: + ievent.reply('email removed') + else: + ievent.reply('delete failed') + +cmnds.add('user-delemail', handle_delemail, 'OPER') +examples.add('user-delemail', 'reset email', 'user-delemail') +aliasset('delemail', 'user-delemail') +tests.add('user-setemail mekker@email').add('user-delemail', 'email removed') + +def handle_addpermit(bot, ievent): + + """ user-addpermit .. add permit to permit list of . """ + + try: + who, what = ievent.args + except ValueError: + ievent.missing(" ") + return + + if not users.exist(who): + ievent.reply("can't find username of %s" % who) + return + + name = users.getname(ievent.userhost) + if users.gotpermit(name, (who, what)): + ievent.reply('%s is already allowed to do %s' % (who, what)) + return + + result = users.addpermit(name, who, what) + + if result: + ievent.reply('permit added') + else: + ievent.reply('add failed') + +cmnds.add('user-addpermit', handle_addpermit, 'USER') +examples.add('user-addpermit', 'user-addpermit .. permit nick access to .. use setperms to add permissions', 'user-addpermit dunker todo') +aliasdel('allow') +tests.add('user-add mtest16 mekker@test16').add('user-addpermit mtest16 todo', 'permit added').add('user-delpermit mtest16 todo').add('delete mtest16') + +def handle_permit(bot, ievent): + + """ user-permit .. get permit list of user giving the command. """ + + if ievent.rest: + ievent.reply("use the user-addpermit command to allow somebody \ +something .. use getname to get the username of somebody .. this \ +command shows what permits you have") + return + + name = users.getname(ievent.userhost) + if not name: + ievent.reply("can't find user for %s" % ievent.userhost) + return + + permits = users.getuserpermits(name) + if permits: + ievent.reply("you permit the following: ", permits, dot=True) + else: + ievent.reply("you don't have any permits") + +cmnds.add('user-permit', handle_permit, 'USER') +examples.add('user-permit', 'show permit of user giving the command', 'user-permit') +aliasset('permit', 'user-permit') +tests.add('user-add mtest17 mekker@test17').add('user-addpermit mtest17 todo', 'permit added').add('user-delpermit mtest17 todo').add('delete mtest17') + +def handle_userdelpermit(bot, ievent): + + """ user-delpermit .. remove (name, permit) from permit list. """ + try: + who, what = ievent.args + except ValueError: + ievent.missing(" ") + return + + if not users.exist(who): + ievent.reply("can't find registered name of %s" % who) + return + + name = users.getname(ievent.userhost) + if not users.gotpermit(name, (who, what)): + ievent.reply('%s is already not allowed to do %s' % (who, what)) + return + + result = users.deluserpermit(name, (who, what)) + + if result: + ievent.reply('%s denied' % what) + else: + ievent.reply('delete failed') + +cmnds.add('user-delpermit', handle_userdelpermit, 'USER') +examples.add('user-delpermit', 'user-delpermit ', 'user-delpermit dunker todo') +aliasdel('deny') +tests.add('user-add mtest18 mekker@test17').add('user-addpermit mtest18 todo', 'permit added').add('user-delpermit mtest18 todo').add('delete mtest18') + +def handle_check(bot, ievent): + + """ user-check .. get user data of . """ + + try: + nick = ievent.args[0] + except IndexError: + ievent.missing('') + return + + userhost = getwho(bot, nick) + if not userhost: + ievent.reply("can't find userhost of %s" % nick) + return + + name = users.getname(userhost) + if not name: + ievent.reply("can't find user") + return + + userhosts = users.getuserhosts(name) + perms = users.getuserperms(name) + email = users.getuseremail(name) + permits = users.getuserpermits(name) + status = users.getuserstatuses(name) + ievent.reply('userrecord of %s = userhosts: %s perms: %s email: %s permits: %s status: %s' % (name, str(userhosts), str(perms), str(email), str(permits), str(status))) + +cmnds.add('user-check', handle_check, 'OPER') +examples.add('user-check', 'user-check ', 'user-check dunker') +aliasset('check', 'user-check') +tests.add('user-add mtest19 mekker@test19').add('user-check mtest19', 'mekker@test19').add('delete mtest19') + +def handle_show(bot, ievent): + + """ user-show .. get data of . """ + + try: + name = ievent.args[0] + except IndexError: + ievent.missing('') + return + + name = name.lower() + if not users.exist(name): + ievent.reply("can't find user %s" % name) + return + + userhosts = str(users.getuserhosts(name)) + perms = str(users.getuserperms(name)) + email = str(users.getuseremail(name)) + permits = str(users.getuserpermits(name)) + status = str(users.getuserstatuses(name)) + ievent.reply('userrecord of %s = userhosts: %s perms: %s email: %s permits: %s status: %s' % (name, userhosts, perms, email, permits, status)) + +cmnds.add('user-show', handle_show, 'OPER') +examples.add('user-show', 'user-show .. show data of ', 'user-show dunker') +tests.add('user-add mtest20 mekker@test20').add('user-show mtest20', 'mekker@test20').add("delete mtest20") + +def handle_match(bot, ievent): + + """ user-match .. get data of . """ + + try: + userhost = ievent.args[0] + except IndexError: + ievent.missing('') + return + + name = users.getname(userhost) + if not name: + ievent.reply("can't find user with userhost %s" % userhost) + return + + userhosts = str(users.gethosts(userhost)) + perms = str(users.getperms(userhost)) + email = str(users.getemail(userhost)) + permits = str(users.getpermits(userhost)) + status = str(users.getstatuses(userhost)) + rlog(10, 'users', "matched called") + ievent.reply('userrecord of %s = userhosts: %s perms: %s email: %s permits: %s status: %s' % (name, userhosts, perms, email, permits, status)) + +cmnds.add('user-match', handle_match, ['USER', 'OPER']) +examples.add('user-match', 'user-match ', 'user-match test@test') +aliasset('match', 'user-match') +tests.add('user-add mtest21 mekker@test21').add('user-match mekker@test21', 'mekker@test21').add("delete mtest21") + +def handle_getuserstatus(bot, ievent): + + """ user-allstatus .. list users with status . """ + + try: + status = ievent.args[0].upper() + except IndexError: + ievent.missing('') + return + + result = users.getstatususers(status) + if result: + ievent.reply("users with %s status: " % status, result, dot=True) + else: + ievent.reply("no users with %s status found" % status) + return + +cmnds.add('user-allstatus', handle_getuserstatus, 'OPER') +examples.add('user-allstatus', 'user-allstatus .. get all users with status', 'user-allstatus #dunkbots') +tests.add('user-allstatus') + +def handle_getuserperm(bot, ievent): + + """ user-allperm .. list users with permission . """ + + try: + perm = ievent.args[0].upper() + except IndexError: + ievent.missing('') + return + + result = users.getpermusers(perm) + + if result: + ievent.reply('users with %s permission: ' % perm, result, dot=True) + else: + ievent.reply("no users with %s permission found" % perm) + return + +cmnds.add('user-allperm', handle_getuserperm, 'OPER') +examples.add('user-allperm', 'user-allperm .. get users with permission', 'user-allperm rss') +tests.add('user-allperm OPER', 'owner') + +def handle_usersearch(bot, ievent): + + """ search for user matching given userhost. """ + + try: + what = ievent.args[0] + except IndexError: + ievent.missing('') + return + + result = users.usersearch(what) + if result: + res = ["(%s) %s" % u for u in result] + ievent.reply('users matching %s: ' % what, res, dot=True) + else: + ievent.reply('no userhost matching %s found' % what) + +cmnds.add('user-search', handle_usersearch, 'OPER') +examples.add('user-search', 'search users userhosts', 'user-search gozerbot') +tests.add('user-search exe', 'exec') + +def handle_addpermall(bot, ievent): + + """ user-addpermall .. add permission to all users. """ + + try: + perm = ievent.args[0].upper() + except IndexError: + ievent.missing('') + return + + if perm == 'OPER': + ievent.reply("can't add OPER permissions to all") + return + + users.addpermall(perm) + ievent.reply('%s perm added' % perm) + +#cmnds.add('user-addpermall', handle_addpermall, 'OPER') +#examples.add('user-addpermall', 'user-addpermall .. add to all users', 'addpermsall USER') +#tests.add('user-addpermall blabla', 'BLABLA perm added').add('user-delpermall blabla') + +def handle_delpermall(bot, ievent): + + """ user-delpermall .. delete permission from all users. """ + + try: + perm = ievent.args[0].upper() + except IndexError: + ievent.missing('') + return + + if perm == 'OPER': + ievent.reply("can't delete OPER permissions from all") + return + + users.delpermall(perm) + ievent.reply('%s perm deleted' % perm) + +#cmnds.add('user-delpermall', handle_delpermall, 'OPER') +#examples.add('user-delpermall', 'user-delpermall .. delete from all users', 'delpermsall BLA') +#tests.add('user-addpermall blabla').add('user-delpermall blabla', 'BLABLA perm deleted') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/tell.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/tell.py @@ -0,0 +1,43 @@ +# gozerbot/plugs/tell.py +# +# + +""" send the output of a command to . """ + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plugins import plugins +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +import copy + +plughelp.add('tell', 'the tell command sends the output of a command to another user') + +def handle_tell(bot, ievent): + + """ send output of command to another user. """ + + try: + nick, cmnd = ievent.rest.split(' ', 1) + except ValueError: + ievent.missing(' ') + return + + event = copy.deepcopy(ievent) + event.txt = cmnd + event.onlyqueues = True + result = plugins.cmnd(bot, event) + + if not result: + ievent.reply("no result for %s" % cmnd) + return + + ievent.reply("%s sends your this: " % ievent.nick, result, nick=nick, \ +dot=True) + ievent.reply("%s item(s) send" % len(result)) + +cmnds.add('tell', handle_tell, 'USER', threaded=True) +examples.add('tell', 'tell .. send output of command to another user', 'tell dunker version') +tests.add('tell dunker version', 'GOZERBOT') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/uniq.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/uniq.py @@ -0,0 +1,45 @@ +# gozerbot/plugs/uniq.py +# +# used in a pipeline .. unique elements """ +# Wijnand 'tehmaze' Modderman - http://tehmaze.com +# BSD License + +""" used in a pipeline .. unique elements """ + +__author__ = "Wijnand 'tehmaze' Modderman - http://tehmaze.com" +__license__ = 'BSD' + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.utils.generic import waitforqueue +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +# basic imports +import sets + +plughelp.add('uniq', 'unique elements of a pipeline') + +def handle_uniq(bot, ievent): + + """ uniq the result list """ + + if not ievent.inqueue: + ievent.reply('use uniq in a pipeline') + return + + result = waitforqueue(ievent.inqueue, 30) + + if not result: + ievent.reply('no data') + return + + result = list(sets.Set(result)) + + if not result: + ievent.reply('no result') + else: + ievent.reply(result, dot=True) + +cmnds.add('uniq', handle_uniq, ['USER', 'WEB', 'CLOUD']) +tests.add('list | uniq') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/to.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/to.py @@ -0,0 +1,53 @@ +# gozerbot/plugs/to.py +# +# + +""" send output to another user .. used in a pipeline. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.utils.generic import getwho, waitforqueue +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +plughelp.add('to', 'send the output to another user .. used in a pipeline') + +def handle_to(bot, ievent): + + """ direct pipeline output to . """ + + if not ievent.inqueue: + ievent.reply('use to in a pipeline') + return + + try: + nick = ievent.args[0] + except IndexError: + ievent.reply('to ') + return + + if nick == 'me': + nick = ievent.nick + + if not getwho(bot, nick): + ievent.reply("don't know %s" % nick) + return + + result = waitforqueue(ievent.inqueue, 5) + + if result: + ievent.reply("%s sends you this:" % ievent.nick, nick=nick) + ievent.reply(result, nick=nick, dot=True) + if len(result) == 1: + ievent.reply('1 element sent') + else: + ievent.reply('%s elements sent' % len(result)) + else: + ievent.reply('nothing to send') + +cmnds.add('to', handle_to, 'USER', threaded=True) +examples.add('to', 'send pipeline output to another user', 'list | to dunker') +tests.add('list | to dunker') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/core.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/core.py @@ -0,0 +1,975 @@ +# plugs/core.py +# +# + +""" core bot commands. """ + + +__copyright__ = 'this file is in the public domain' +__gendocskip__ = ['userhostcache', ] + +# gozerbot imports +from gozerbot.utils.generic import getwho, die +from gozerbot.utils.timeutils import elapsedstring +from gozerbot.reboot import reboot_stateful, reboot +from gozerbot.utils.generic import getversion, cleanpyc +from gozerbot.utils.exception import handle_exception +from gozerbot.exit import globalshutdown +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.callbacks import callbacks +from gozerbot.redispatcher import rebefore, reafter +from gozerbot.plugins import plugins +from gozerbot.users import users +from gozerbot.partyline import partyline +from gozerbot.fleet import fleet +from gozerbot.config import config +from gozerbot.utils.statdict import Statdict +from gozerbot.aliases import aliases, aliasreverse, aliasset +from gozerbot.partyline import partyline +from gozerbot.plughelp import plughelp +from gozerbot.eventhandler import mainhandler +from gozerbot.runner import cbrunners, cmndrunners +from gozerbot.tests import tests +import gozerbot.utils.log + +from simplejson import loads + +# basic imports +from sets import Set +import time, threading, sys, re, os, copy + +plughelp.add('core', 'core commands for the bot') + +try: + pluginlist = loads(open('pluginlist').read()) +except: + pluginlist = [] + +def handle_rusage(bot, ievent): + + """ show resource usage. """ + + try: + import resource + ievent.reply('resources: %s' % str(resource.getrusage(resource.RUSAGE_SELF))) + except ImportError: + ievent.reply('this commands only works on unix/linux') + +cmnds.add('rusage', handle_rusage, 'OPER') +examples.add('rusage', 'show rusage of the bot', 'rusage') +tests.add('rusage', 'ru_stime') + +def handle_options(bot, ievent): + + """ show possible options of a command. """ + + if not ievent.rest: + ievent.missing('') + return + + options = plugins.getoptions(ievent.rest) + + if options: + ievent.reply("the following options are available for command %s: " % \ +ievent.rest, options.keys()) + else: + ievent.reply('no command options found for %s' % ievent.rest) + +cmnds.add('options', handle_options, ['USER', 'OPER']) +examples.add('options', 'show what options are available for a command', 'options list') +tests.add('options list', '--chan') + +def handle_whatperms(bot, ievent): + + """ show possible permissions. """ + + ievent.reply("the following permission are available: ", plugins.whatperms()) + +cmnds.add('whatperms', handle_whatperms, ['USER', 'OPER']) +examples.add('whatperms', 'show what permissions are available', 'whatperms') +tests.add('whatperms', 'USER') + +def handle_encoding(bot, ievent): + + """ show default encoding. """ + + ievent.reply('default encoding is %s' % sys.getdefaultencoding()) + +cmnds.add('encoding', handle_encoding, ['USER', 'OPER']) +examples.add('encoding', 'show default encoding', 'encoding') +tests.add('encoding') + +def handle_uptime(bot, ievent): + + """ show uptime. """ + + ievent.reply("uptime is %s" % elapsedstring(time.time()-bot.starttime)) + +cmnds.add('uptime', handle_uptime, ['USER', 'WEB', 'JCOLL']) +examples.add('uptime', 'show uptime of the bot', 'uptime') +aliasset('up', 'uptime') +tests.add('uptime', 'uptime is') + +def handle_userhostcache(bot, ievent): + + """ show bots userhost cache. """ + + ievent.reply(str(bot.userhosts.data)) + +cmnds.add('userhostcache', handle_userhostcache, 'OPER') +tests.add('userhostcache', 'exec') + +def handle_list(bot, ievent): + + """ list [] .. list loaded plugins or list commands provided by \ + plugin. + """ + global pluginlist + + try: + what = ievent.args[0] + except IndexError: + # no arguments given .. show plugins + ievent.reply('loaded plugins: ', plugins.list()) + return + + # show commands of plugin + result = [] + + for i, j in cmnds.items(): + if what == j.plugname: + txt = "" + alias = aliasreverse(i) + if alias: + txt += "%s (%s)" % (i, alias) + else: + txt = i + if txt: + result.append(txt) + + if result: + result.sort() + ievent.reply('%s has the following commands: ' % what, result) + else: + ievent.reply('no commands found for plugin %s' % what) + +cmnds.add('list', handle_list, ['USER', 'WEB', 'CLOUD'], threaded=True) +examples.add('list', 'list registered plugins or list commands in plugin', '1) list 2) list rss') +tests.add('list', 'core') + +def handle_available(bot, ievent): + + """ available .. show what plugins are available. """ + + l = Set(plugins.list()) + ondisk = Set(plugins.available()) + diff = list(ondisk - l) + diff.sort() + ievent.reply('\002available plugins:\002 (reload to enable): ', diff) + +cmnds.add('available', handle_available, ['USER', 'WEB']) +examples.add('available', 'show what plugins are available but not loaded (see the list command for loaded plugins)', 'available') +aliasset('avail', ' available') +aliasset('plugins', 'available') +tests.add('avail') + +def doquit(bot, ievent): + + """ quit the bot. """ + + globalshutdown() + +cmnds.add('quit', doquit, 'OPER') +examples.add('quit', 'quit the bot', 'quit') +aliasset('shutdown', 'quit') +aliasset('exit', 'quit') +aliasset('halt', 'quit') + +def handle_reboot(bot, ievent): + + """ reboot .. reboot the bot. """ + + ievent.reply('rebooting') + global backupstop + stateful = True + + if ievent.rest == 'cold': + stateful = False + + time.sleep(2) + + try: + backupstop = 1 + if not stateful: + fleet.exit() + else: + fleet.exit(jabber=True) + plugins.exit() + finally: + if stateful: + mainhandler.put(0, reboot_stateful, bot, ievent, fleet, partyline) + else: + mainhandler.put(0, reboot) + +cmnds.add('reboot', handle_reboot, 'OPER') +examples.add('reboot', 'restart the bot', 'reboot') + +def handle_commands(bot, ievent): + + """ commands .. show commands of . """ + + try: + plugin = ievent.args[0].lower() + except IndexError: + ievent.missing(' .. see the list command for available \ +plugins') + return + + if not plugins.plugs.has_key(plugin): + ievent.reply('no %s plugin is loaded .. see the available command for \ +available plugins (reload to enable)' % plugin) + return + + result = [] + + cp = dict(cmnds) + for i, j in cp.iteritems(): + if plugin == j.plugname: + txt = "" + alias = aliasreverse(i) + if alias: + txt += "%s (%s)" % (i, alias) + else: + txt = i + if txt: + result.append(txt) + + if result: + result.sort() + ievent.reply('%s has the following commands: ' % plugin, result, \ +dot=True) + else: + ievent.reply('no commands found for plugin %s' % plugin) + +cmnds.add('commands', handle_commands, ['USER', 'WEB', 'CLOUD']) +examples.add('commands', 'show commands of ', '1) commands core') +tests.add('commands core', 'uptime') + +def handle_perm(bot, ievent): + + """ perm .. get permission of command. """ + + try: + cmnd = ievent.args[0] + except IndexError: + ievent.missing("") + return + + try: + cmnd = aliases.data[cmnd] + except KeyError: + pass + + perms = cmnds.perms(cmnd) + + if perms: + ievent.reply("%s command needs %s permission" % (cmnd, perms)) + return + + ievent.reply("can't find perm for %s" % cmnd) + +cmnds.add('perm', handle_perm, ['USER', 'WEB']) +examples.add('perm', 'show permission of command', 'perm quit') +tests.add('perm version', 'USER') + +def handle_cc(bot, ievent): + + """ cc [] .. set/get control character of channel. """ + + try: + chan = ievent.args[1].lower() + except IndexError: + chan = ievent.channel.lower() + + try: + what = ievent.args[0] + + if not users.allowed(ievent.userhost, 'OPER'): + return + + if len(what) > 1: + ievent.reply("only one character is allowed") + return + + try: + bot.channels[chan]['cc'] = what + except (KeyError, TypeError): + ievent.reply("no channel %s in database" % chan) + return + + bot.channels.save() + ievent.reply('control char set to %s' % what) + except IndexError: + # no argument given .. show cc of channel command is given in + try: + cchar = bot.channels[chan]['cc'] + ievent.reply('control character(s) for channel %s are/is %s' % \ +(chan, cchar)) + except (KeyError, TypeError): + ievent.reply("default cc is %s" % config['defaultcc']) + +cmnds.add('cc', handle_cc, 'USER', allowqueue=False) +examples.add('cc', 'set control char of channel or show control char of channel','1) cc ! 2) cc') +tests.add('cc --chan #dunkbots', '!') +tests.add('cc --chan #dunkbots !', '!') + +def handle_ccadd(bot, ievent): + + """ add a control char to the channels cc list. """ + + try: + chan = ievent.args[1].lower() + except IndexError: + chan = ievent.channel.lower() + + try: + what = ievent.args[0] + + if not users.allowed(ievent.userhost, 'OPER'): + return + + if len(what) > 1: + ievent.reply("only one character is allowed") + return + + try: + bot.channels[chan]['cc'] += what + except (KeyError, TypeError): + ievent.reply("no channel %s in database" % chan) + return + + bot.channels.save() + ievent.reply('control char %s added' % what) + except IndexError: + ievent.missing(' []') + +cmnds.add('cc-add', handle_ccadd, 'OPER', allowqueue=False) +examples.add('cc-add', 'cc-add .. add control character', 'cc-add #') +tests.add('cc-add --chan #dunkbots @', '@').add('cc-del --chan #dunkbots @') + +def handle_ccdel(bot, ievent): + + """ remove a control char from the channels cc list. """ + + try: + chan = ievent.args[1].lower() + except IndexError: + chan = ievent.channel.lower() + + try: + what = ievent.args[0] + + if not users.allowed(ievent.userhost, 'OPER'): + return + + if len(what) > 1: + ievent.reply("only one character is allowed") + return + + try: + bot.channels[chan]['cc'] = \ +bot.channels[chan]['cc'].replace(what, '') + except KeyError: + ievent.reply("no channel %s in database") + return + except TypeError: + ievent.reply("no channel %s in database" % chan) + return + + bot.channels.save() + ievent.reply('control char %s deleted' % what) + + except IndexError: + ievent.missing(' []') + +cmnds.add('cc-del', handle_ccdel, 'OPER') +examples.add('cc-del', 'cc-del .. remove cc', 'cc-del #') +tests.add('cc-add --chan #dunkbots @').add('cc-del --chan #dunkbots @', '@') + +def handle_intro(bot, ievent): + + """ intro .. do whois on nick to sync userhosts cache. """ + + if not bot.type == 'irc': + ievent.reply("intro only works on irc bots") + return + + try: + who = ievent.args[0] + except IndexError: + ievent.reply("intro ") + return + + bot.whois(who) + ievent.reply('whois command send') + +cmnds.add('intro', handle_intro, 'OPER') +examples.add('intro', 'do a whois of to sync userhost into the userhost cache', 'intro dunker') +tests.add('intro dunker') + +def handle_loglevel(bot, ievent): + + """ loglevel .. get or set loglevel. the lower the more the bot logs. """ + + if len(ievent.args) == 0: + ievent.reply('loglevel is %s' % str(gozerbot.utils.log.loglevel)) + return + + try: + level = int(ievent.args[0]) + except ValueError: + ievent.reply('i need a integer argument') + return + + config.set('loglevel', level) + gozerbot.utils.log.loglevel = level + ievent.reply('loglevel is now %s' % config['loglevel']) + +cmnds.add('loglevel', handle_loglevel,'OPER') +examples.add('loglevel', 'get/set current loglevel .. the lower the loglevel the more the bot logs', '1) loglevel 2) loglevel 10') +tests.add('loglevel 10', 'is now 10').add('loglevel', '10') + +def handle_loglist(bot, ievent): + + """ loglist .. get or set loglist .. loglist is a list of plugins + to log. + """ + + if config['loglist'] == None: + config['loglist'] = [] + + for plugin in ievent.args: + if plugin not in config['loglist']: + config['loglist'].append(plugin) + if plugin not in gozerbot.utils.log.loglist: + gozerbot.utils.log.loglist.append(plugin) + config.save() + + ievent.reply('loglist is now %s' % config['loglist']) + +cmnds.add('loglist', handle_loglist,'OPER') +examples.add('loglist', 'get loglist or add plugin to loglist', '1) loglist 2) loglist rss') +tests.add('loglist rss', 'rss').add('loglist', 'rss') + +def handle_loglistdel(bot, ievent): + + """ loglist-del .. delete plugin from loglist. """ + + if config['loglist'] == None: + config['loglist'] = [] + + for plugin in ievent.args: + + try: + config['loglist'].remove(plugin) + except ValueError: + pass + + try: + gozerbot.utils.log.loglist.remove(plugin) + except ValueError: + pass + + config.save() + + ievent.reply('loglist is now %s' % config['loglist']) + +cmnds.add('loglist-del', handle_loglistdel,'OPER') +examples.add('loglist-del', 'remove plugin from loglist', 'loglist-del rss') +tests.add('loglist rss', 'rss').add('loglist-del') + +def handle_partylist(bot, ievent): + + """ partylist .. show users on partylist. """ + + partynicks = partyline.list_nicks() + + if partynicks: + ievent.reply("people on partyline: ", partynicks) + else: + ievent.reply('no party yet!') + +cmnds.add('partylist', handle_partylist, ['USER', 'WEB']) +examples.add('partylist', 'show connected partylist users', 'partylist') +tests.add('partylist') + +def handle_partysilent(bot, ievent): + + """ party-silent .. disable partyline noise. """ + + partyline.silent(ievent.nick) + ievent.reply('partyline put to silent mode') + +cmnds.add('party-silent', handle_partysilent, ['USER', ]) +examples.add('party-silent', 'disable partyline noise', 'party-silent') +tests.add('party-silent') + +def handle_partyloud(bot, ievent): + + """ party-loud .. enable partyline noise. """ + + partyline.loud(ievent.nick) + ievent.reply('partyline put to loud mode') + +cmnds.add('party-loud', handle_partyloud, ['USER', ]) +examples.add('party-loud', 'enable partyline noise', 'party-loud') +tests.add('party-loud') + +def handle_threads(bot, ievent): + + """ show running threads. """ + + stats = Statdict() + threadlist = threading.enumerate() + + for thread in threadlist: + stats.upitem(thread.getName()) + + result = [] + + for item in stats.top(): + result.append("%s = %s" % (item[0], item[1])) + + result.sort() + ievent.reply("threads running: ", result) + +cmnds.add('threads', handle_threads, ['USER', 'OPER']) +examples.add('threads', 'show running threads', 'thread') +tests.add('threads', 'Bot') + +def handle_running(bot, ievent): + + """ running .. show running jobs on the runner. """ + + stats = [Statdict() for i in range(10)] + teller = 0 + + for runner in cbrunners.running(): + stats[teller].upitem(runner) + teller += 1 + + teller = 0 + + for runner in cmndrunners.running(): + stats[teller].upitem(runner) + teller += 1 + + result = [] + + for i in range(teller): + for item in stats[i].top(): + result.append("%s.%s = %s" % (item[0], 10-i, item[1])) + + result.sort() + ievent.reply("runners: ", result) + +cmnds.add('running', handle_running, ['USER', 'OPER']) +examples.add('running', 'show running jobs', 'running') +tests.add('running') + +def handle_nowrunning(bot, ievent): + result = [] + + for runner in cbrunners.runners: + result.append(runner.nowrunning) + + ievent.reply('nowrunning callbacks: %s' % str(result)) + result = [] + + for runner in cmndrunners.runners: + result.append(runner.nowrunning) + + ievent.reply('nowrunning commands: %s' % str(result)) + +cmnds.add('nowrunning', handle_nowrunning, 'OPER') +tests.add('nowrunning') + +def handle_callbacks(bot, ievent): + + """ list all callbacks. """ + + ievent.reply(callbacks.list()) + +cmnds.add('callbacks', handle_callbacks, 'OPER') +tests.add('callbacks') + +def handle_save(bot, ievent): + + """ save bot data to disk. """ + + ievent.reply('saving') + plugins.save() + fleet.save() + config.save() + ievent.reply('done') + +cmnds.add('save', handle_save, 'OPER') +examples.add('save', 'save bot data', 'save') +tests.add('save', 'done') + +def handle_version(bot, ievent): + + """ show bot's version. """ + + ievent.reply(getversion()) + +cmnds.add('version', handle_version, ['USER', 'WEB', 'JCOLL', 'CLOUD']) +examples.add('version', 'show version of the bot', 'version') +aliasset('v', 'version') +tests.add('version', 'GOZERBOT') + +def handle_whereis(bot, ievent): + + """ whereis .. locate a command. """ + + try: + cmnd = ievent.args[0] + except IndexError: + ievent.missing('') + return + + plugs = plugins.whereis(cmnd) + + if plugs: + ievent.reply("%s command is in: " % cmnd, plugs) + else: + ievent.reply("can't find " + cmnd) + +cmnds.add('whereis', handle_whereis, ['USER', 'WEB']) +examples.add('whereis', 'whereis .. show in which plugins is', 'whereis test') +tests.add('whereis version', 'core') + +def handle_help(bot, ievent): + + """ help [|] .. show help on plugin/command or show basic help msg. """ + + try: + what = ievent.args[0] + except IndexError: + ievent.reply('run help on one of the following plugins: ', pluginlist) + return + + plugins.needreloadcheck(bot, ievent, what) + phelp = plughelp.get(what) + cmndresult = [] + + if phelp: + ievent.reply('plugin description: %s' % phelp) + + perms = list(users.getperms(ievent.userhost)) + + for i, j in cmnds.iteritems(): + if what == j.plugname: + for perm in j.perms: + if perm in perms: + if i not in cmndresult: + cmndresult.append(i) + + if cmndresult: + cmndresult.sort() + resultstr = "" + for i in cmndresult: + alias = aliasreverse(i) + if alias: + resultstr += "%s (%s) .. " % (i, alias) + else: + resultstr += "%s .. " % i + ievent.reply('commands: %s'\ + % resultstr[:-4]) + else: + ievent.reply('no commands available for permission %s' % \ +str(perms)) + + result = [] + + for i in rebefore.relist: + if what == i.plugname: + if users.allowed(ievent.userhost, i.perms): + result.append(i.regex) + + for i in reafter.relist: + if what == i.plugname: + if users.allowed(ievent.userhost, i.perms): + result.append(i.regex) + + if result: + resultstr = "" + for i in result: + resultstr += '"%s" .. ' % i + ievent.reply('regular expressions: %s' % resultstr[:-4]) + else: + pass + + result = [] + + for i, j in callbacks.cbs.items(): + for z in j: + if what == z.plugname: + result.append(i) + + if result: + resultstr = "" + for i in result: + resultstr += "%s .. " % i + ievent.reply('callbacks: %s' % resultstr[:-4]) + else: + pass + + if not cmndresult: + return + + if what in aliases.data: + ievent.reply('%s is an alias for %s' % (what, aliases.data[what])) + what = aliases.data[what] + + try: + example = examples[what] + except KeyError: + return + + ievent.reply('%s .. alias: %s .. examples: %s' % (example.descr, aliasreverse(what), example.example)) + +cmnds.add('help', handle_help, ['USER', 'WEB']) +examples.add('help', 'get help on or ', '1) help test 2) help misc') +tests.add('help core', 'version') + +def handle_apro(bot, ievent): + + """ apro .. apropos for command. """ + + try: + what = ievent.args[0] + except IndexError: + ievent.missing('') + return + + result = [] + perms = users.getperms(ievent.userhost) + + for i in cmnds.apropos(re.escape(what), perms=perms): + alias = aliasreverse(i) + if alias: + result.append('%s (%s)' % (i, alias)) + else: + result.append(i) + + res = [] + + for alias in aliases.data: + if what in alias and not what in result: + res.append(alias) + + result.extend(res) + + if result: + ievent.reply("commands matching %s: " % what, result , nr=1) + else: + ievent.reply('no matching commands found for %s' % what) + +cmnds.add('apro', handle_apro, ['USER', 'WEB']) +aliases.data['apropos'] = 'apro' +examples.add('apro', 'apro .. search for commands that contain ', 'apro com') +tests.add('apro ver', 'version') + +def handle_u(bot, ievent): + + """ u .. show userhost entry in cache. """ + + try: + nick = ievent.args[0] + except IndexError: + ievent.missing('') + return + + result = getwho(bot, nick) + + if result: + ievent.reply(result) + else: + ievent.reply("can't get userhost for %s" % nick) + +cmnds.add('u', handle_u, ['USER', ]) +examples.add('u', 'u .. get userhost cache entry for ', 'u dunker') +tests.add('u dunker') + +def handle_less(bot, ievent): + + """ get entry from the output cache. """ + + try: + if len(ievent.args) == 3: + (who, index1, index2) = ievent.args + elif len(ievent.args) == 2: + if bot.jabber: + who = ievent.userhost + else: + who = ievent.nick + (index1, index2) = ievent.args + else: + if bot.jabber: + who = ievent.userhost + else: + who = ievent.nick + index1 = 0 + index2 = ievent.args[0] + index1 = int(index1) + index2 = int(index2) + except IndexError: + ievent.missing('[] [] ') + return + except ValueError: + ievent.reply('i need integers as arguments') + return + + txt = bot.less.get(who, index1, index2) + + if not txt: + ievent.reply('no data available for %s %s %s' % \ +(who, index1, index2)) + return + + if bot.jabber: + bot.say(ievent.userhost, txt) + else: + ievent.reply(txt) + +cmnds.add('less', handle_less, ['USER', 'CLOUD']) +examples.add('less', "less [] [] .. get txt from bots output cache", '1) less 0 2) less 0 2 3) less bart 1 0') +tests.add('less 0 0') + +def handle_lesssize(bot, ievent): + + """ show size of output cache. """ + + try: + who = ievent.args[0] + except IndexError: + who = ievent.nick + + ievent.reply(bot.less.size(who)) + +cmnds.add('less-size', handle_lesssize, ['USER', ]) +examples.add('less-size', "show sizes of data in bot's ouput cache", 'less-size') +tests.add('less-size') + +def handle_more(bot, ievent): + + """ pop message from the output cache. """ + + try: + who = ievent.args[0] + except IndexError: + if bot.jabber: + who = ievent.userhost + else: + who = ievent.nick + + what, size = bot.less.more(who, 0) + + if not what: + ievent.reply('no more data available for %s' % who) + return + + if bot.jabber: + if size: + bot.say(ievent.userhost, "%s (+%s)" % (what, size)) + else: + ievent.reply(what) + return + + if size: + + # check if bot is in notice mode + notice = False + + try: + notice = bot.channels[ievent.printto]['notice'] + except (KeyError, TypeError): + pass + + # check if bot is in silent mode + silent = False + + try: + silent = bot.channels[ievent.printto]['silent'] + except (KeyError, TypeError): + pass + + if notice: + how = 'notice' + else: + how = 'msg' + + if silent: + printto = ievent.nick + else: + printto = ievent.printto + + bot.output(printto,"%s \002(+%s)\002" % (what, size), how) + else: + ievent.reply(what) + +cmnds.add('more', handle_more, ['USER', 'CLOUD'], threaded=True) +examples.add('more', 'return txt from output cache', '1) more 2) more test') +tests.add('more') + +def handle_whatcommands(bot, ievent): + + """ show all commands with permission. """ + + if not ievent.rest: + ievent.missing('') + return + + result = cmnds.list(ievent.rest) + result.sort() + + if not result: + ievent.reply('no commands known for permission %s' % ievent.rest) + else: + ievent.reply('commands known for permission %s: ' % ievent.rest, result) + +cmnds.add('whatcommands', handle_whatcommands, 'USER') +examples.add('whatcommands', 'show commands with permission ', 'whatcommands USER') +tests.add('whatcommands USER', 'version') + +def handle_cleanpyc(bot, ievent): + + """ clean pyc files. """ + + removed = cleanpyc() + ievent.reply('removed: ', removed, dot=True) + +cmnds.add('cleanpyc', handle_cleanpyc, 'OPER') +tests.add('cleanpyc') + +def handle_versions(bot, ievent): + + """ show versions of all loaded modules (if available). """ + + versions = {} + for mod in copy.copy(sys.modules): + try: + versions[mod] = sys.modules[mod].__version__ + except AttributeError, ex: + pass + try: + versions['python'] = sys.version + except AttributeError, ex: + pass + ievent.reply("versions ==> ", versions) + + +cmnds.add('versions', handle_versions, 'OPER') +examples.add('versions', 'show versions of all loaded modules', 'versions') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/irc.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/irc.py @@ -0,0 +1,495 @@ +# plugs/irc.py +# +# + +""" irc related commands. """ + +__copyright__ = 'this file is in the public domain' +__gendocfirst__ = ['reconnect', 'join'] +__gendoclast__ = ['part', ] + +# gozerbot imports +from gozerbot.callbacks import callbacks +from gozerbot.fleet import fleet +from gozerbot.partyline import partyline +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests +import gozerbot.threads.thr as thr + +# basic imports +import Queue, sets + +plughelp.add('irc', 'irc related commands') + +ignorenicks = [] + +def handle_broadcast(bot, ievent): + + """ broadcast txt to all joined channels. """ + + if not ievent.rest: + ievent.missing('') + return + + ievent.reply('broadcasting') + fleet.broadcast(ievent.rest) + partyline.say_broadcast(ievent.rest) + ievent.reply('done') + +cmnds.add('broadcast', handle_broadcast, 'OPER') +examples.add('broadcast', 'send a message to all channels and dcc users', 'broadcast good morning') +tests.add('broadcast testing testing') + +def handle_alternick(bot, ievent): + + """ set alternative nick used if nick is already taken. """ + + try: + nick = ievent.args[0] + except IndexError: + ievent.reply('alternick is %s' % bot.state['alternick']) + return + + ievent.reply('changing alternick to %s' % nick) + bot.state['alternick'] = nick + bot.state.save() + ievent.reply('done') + +cmnds.add('alternick', handle_alternick, 'OPER') +examples.add('alternick', 'get/set alertnick' , '1) alternick 2) alternick gozerbot2') +tests.add('alternick testbot', 'testbot') + +def dojoin(bot, ievent): + + """ join [password]. """ + + try: + channel = ievent.args[0] + except IndexError: + ievent.missing(" [password]") + return + + try: + password = ievent.args[1] + except IndexError: + password = None + + ievent.reply('joining %s' % channel) + bot.join(channel, password=password) + ievent.done() + +cmnds.add('join', dojoin, ['OPER', 'JOIN']) +examples.add('join', 'join [password]', '1) join #test 2) join #test mekker') +tests.add('join #dunkbots') + +def delchan(bot, ievent): + + """ delchan .. remove channel from bot.channels. """ + + try: + chan = ievent.args[0].lower() + except IndexError: + ievent.missing("") + return + + try: + bot.state['joinedchannels'].remove(chan) + bot.state.save() + except ValueError: + pass + + try: + del bot.channels.data[chan] + bot.channels.save() + except KeyError: + ievent.reply("no channel %s in database" % chan) + + ievent.done() + +cmnds.add('delchan', delchan, 'OPER') +examples.add('delchan', 'delchan .. remove channel from bot.channels', 'delchan #mekker') + +def dopart(bot, ievent): + + """ part []. """ + + if not ievent.rest: + chan = ievent.channel + else: + chan = ievent.rest + + ievent.reply('leaving %s chan' % chan) + bot.part(chan) + ievent.done() + +cmnds.add('part', dopart, 'OPER') +examples.add('part', 'part []', '1) part 2) part #test') +tests.add('part #mekker') + +def handle_channels(bot, ievent): + + """ channels .. show joined channels. """ + + chans = bot.state['joinedchannels'] + + if chans: + ievent.reply("joined channels: ", chans, dot=True) + else: + ievent.reply('no channels joined') + +cmnds.add('channels', handle_channels, ['USER', 'WEB']) +examples.add('channels', 'show what channels the bot is on', 'channels') +tests.add('channels', '#dunkbots') + +def handle_chat(bot, ievent): + + """ chat .. start a bot initiated dcc chat session. """ + + if not bot.type == 'irc': + ievent.reply("chat only works on irc bots") + return + + i = ievent + thr.start_new_thread(bot._dcclisten, (i.nick, i.userhost, i.channel)) + ievent.reply('dcc chat request sent') + +cmnds.add('chat', handle_chat, 'USER') +examples.add('chat', 'start a dcc chat session', 'chat') +tests.add('chat', 'sent') + +def handle_cycle(bot, ievent): + + """ cycle .. recycle channel. """ + + ievent.reply('cycling %s' % ievent.channel) + bot.part(ievent.channel) + try: + key = bot.channels[ievent.channel.lower()]['key'] + except (KeyError, TypeError): + key = None + + bot.join(ievent.channel, password=key) + ievent.done() + +cmnds.add('cycle', handle_cycle, 'OPER') +examples.add('cycle', 'part/join channel', 'cycle') +tests.add('cycle') + +def handle_jump(bot, ievent): + + """ jump .. change server. """ + + if bot.jabber: + ievent.reply('jump only works on irc bots') + return + if len(ievent.args) != 2: + ievent.missing(' ') + return + (server, port) = ievent.args + ievent.reply('changing to server %s' % server) + bot.shutdown() + bot.server = server + bot.port = port + bot.connect() + ievent.done() + +cmnds.add('jump', handle_jump, 'OPER') +examples.add('jump', 'jump .. switch server', 'jump localhost 6667') + +def modecb(bot, ievent): + + """ callback to detect change of channel key. """ + + if ievent.postfix.find('+k') != -1: + key = ievent.postfix.split('+k')[1] + bot.channels[ievent.channel.lower()]['key'] = key + +callbacks.add('MODE', modecb) + +def handle_nick(bot, ievent): + + """ nick .. change bot's nick. """ + + if bot.jabber: + ievent.reply('nick works only on irc bots') + return + + try: + nick = ievent.args[0] + except IndexError: + ievent.missing('') + return + + ievent.reply('changing nick to %s' % nick) + bot.donick(nick, setorig=1, save=1) + ievent.done() + +cmnds.add('nick', handle_nick, 'OPER', threaded=True) +examples.add('nick', 'nick .. set nick of the bot', 'nick mekker') +tests.add('nick miep') + +def handle_sendraw(bot, ievent): + + """ sendraw .. send raw text to the server. """ + + ievent.reply('sending raw txt') + bot.sendraw(ievent.rest) + ievent.done() + +cmnds.add('sendraw', handle_sendraw, 'SENDRAW') +examples.add('sendraw', 'sendraw .. send raw string to the server', \ +'sendraw PRIVMSG #test :yo!') + +def handle_nicks(bot, ievent): + + """ return nicks on channel. """ + + if bot.jabber: + ievent.reply('nicks only works on irc bots') + return + + try: + chan = ievent.args[0] + except IndexError: + chan = ievent.channel + + queue = Queue.Queue() + # set callback for name info response + wait353 = bot.wait.register('353', chan, queue) + # 366 is end of names response list + wait366 = bot.wait.register('366', chan, queue) + result = "" + ievent.reply('searching for nicks') + bot.names(chan) + + while(1): + qres = queue.get() + if qres == None: + break + if qres.cmnd == '366': + break + else: + result += "%s " % qres.txt + + bot.wait.delete(wait353) + bot.wait.delete(wait366) + + if result: + res = result.split() + + for nick in res: + for i in ignorenicks: + if i in nick: + try: + res.remove(nick) + except ValueError: + pass + + res.sort() + ievent.reply("nicks on %s (%s): " % (chan, bot.server), res) + else: + ievent.reply("can't get nicks of channel %s" % chan) + +cmnds.add('nicks', handle_nicks, ['OPER', 'WEB'], threaded=True) +examples.add('nicks', 'show nicks on channel the command was given in', 'nicks') +tests.add('nicks', "can't") + +def handle_silent(bot, ievent): + + """ set silent mode of channel. """ + + if ievent.rest: + channel = ievent.rest.split()[0].lower() + else: + if ievent.cmnd == 'DCC': + return + channel = ievent.channel + + ievent.reply('putting %s to silent mode' % channel) + + try: + bot.channels[channel]['silent'] = 1 + except (KeyError, TypeError): + ievent.reply("no %s channel in database" % channel) + return + ievent.done() + +cmnds.add('silent', handle_silent, 'OPER') +examples.add('silent', 'set silent mode on channel the command was given in', 'silent') +tests.add('silent').add('loud') + +def handle_loud(bot, ievent): + + """ loud .. enable output to the channel. """ + + if ievent.rest: + channel = ievent.rest.split()[0].lower() + else: + if ievent.cmnd == 'DCC': + return + channel = ievent.channel + + ievent.reply('putting %s into loud mode' % ievent.channel) + + try: + bot.channels[channel]['silent'] = 0 + except (KeyError, TypeError): + ievent.reply("no %s channel in database" % channel) + return + + ievent.done() + +cmnds.add('loud', handle_loud, 'OPER') +examples.add('loud', 'disable silent mode of channel command was given in', 'loud') +tests.add('loud') + +def handle_withnotice(bot, ievent): + + """ withnotice .. make bot use notice in channel. """ + + if ievent.rest: + channel = ievent.rest.split()[0].lower() + else: + if ievent.cmnd == 'DCC': + return + channel = ievent.channel + + ievent.reply('setting notice in %s' % channel) + + try: + bot.channels[channel]['notice'] = 1 + except (KeyError, TypeError): + ievent.reply("no %s channel in database" % channel) + return + + ievent.done() + +cmnds.add('withnotice', handle_withnotice, 'OPER') +examples.add('withnotice', 'make bot use notice on channel the command was given in', 'withnotice') +tests.add('withnotice').add('withprivmsg') + +def handle_withprivmsg(bot, ievent): + + """ withprivmsg .. make bot use privmsg in channel. """ + + if ievent.rest: + channel = ievent.rest.split()[0].lower() + else: + if ievent.cmnd == 'DCC': + return + channel = ievent.channel + + ievent.reply('setting privmsg in %s' % ievent.channel) + + try: + bot.channels[channel]['notice'] = 0 + except (KeyError, TypeError): + ievent.reply("no %s channel in database" % channel) + return + + ievent.done() + +cmnds.add('withprivmsg', handle_withprivmsg, 'OPER') +examples.add('withprivmsg', 'make bot use privmsg on channel command was given in', 'withprivmsg') +tests.add('withprivmsg') + +def handle_reconnect(bot, ievent): + + """ reconnect .. reconnect to server. """ + + ievent.reply('reconnecting') + bot.reconnect() + ievent.done() + +cmnds.add('reconnect', handle_reconnect, 'OPER', threaded=True) +examples.add('reconnect', 'reconnect to server', 'reconnect') + +def handle_channelmode(bot, ievent): + + """ show channel mode. """ + + if bot.type != 'irc': + ievent.reply('channelmode only works on irc bots') + return + + try: + chan = ievent.args[0].lower() + except IndexError: + chan = ievent.channel.lower() + + if not chan in bot.state['joinedchannels']: + ievent.reply("i'm not on channel %s" % chan) + return + + ievent.reply('channel mode of %s is %s' % (chan, bot.channels.get(chan, 'mode'))) + +cmnds.add('channelmode', handle_channelmode, 'OPER') +examples.add('channelmode', 'show mode of channel', '1) channelmode 2) channelmode #test') +tests.add('channelmode --chan #dunkbots') + +def handle_action(bot, ievent): + + """ make the bot send an action string. """ + + try: + channel, txt = ievent.rest.split(' ', 1) + except ValueError: + ievent.missing(' ') + return + + bot.action(channel, txt) + +cmnds.add('action', handle_action, ['ACTION', 'OPER'], speed=1) +examples.add('action', 'send an action message', 'action #test yoo dudes') +tests.add('action #dunkbots mekker') + +def handle_say(bot, ievent): + + """ make the bot say something. """ + + try: + channel, txt = ievent.rest.split(' ', 1) + except ValueError: + ievent.missing(' ') + return + + bot.say(channel, txt) + +cmnds.add('say', handle_say, ['SAY', 'OPER'], speed=1) +examples.add('say', 'send txt to channel/user', 'say #test good morning') +tests.add('say #dunkbots mekkerbot') + +def handle_server(bot, ievent): + + """ show the server to which the bot is connected. """ + + ievent.reply(bot.server) + +cmnds.add('server', handle_server, 'OPER') +examples.add('server', 'show server hostname of bot', 'server') +tests.add('server') + +def handle_voice(bot, ievent): + + """ give voice. """ + + if bot.type != 'irc': + ievent.reply('voice only works on irc bots') + return + + if len(ievent.args)==0: + ievent.missing('') + return + + ievent.reply('setting voide on %s' % str(ievent.args)) + + for nick in sets.Set(ievent.args): + bot.voice(ievent.channel, nick) + + ievent.done() + +cmnds.add('voice', handle_voice, 'OPER') +examples.add('voice', 'give voice to user', 'voice test') +tests.add('voice dunker') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/sort.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/sort.py @@ -0,0 +1,111 @@ +# gozerbot/plugs/sort.py +# +# Sorting + +""" sort bot results. """ + +__author__ = "Wijnand 'maze' Modderman " +__license__ = "BSD" + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.utils.generic import waitforqueue +from gozerbot.plughelp import plughelp +from gozerbot.examples import examples +from gozerbot.tests import tests + +# basic imports +import optparse + +plughelp.add('sort', ' sort elements (of a pipeline)') + +class SortError(Exception): pass + +class SortOptionParser(optparse.OptionParser): + + """ options parsers for the sort command. """ + + def __init__(self): + optparse.OptionParser.__init__(self) + + self.add_option('-f', '--ignore-case', + help='fold lower case to upper case characters', default=False, + action='store_true', dest='ignorecase') + + self.add_option('-n', '--numeric-sort', default=False, + help='compare according to string numerical value', + action='store_true', dest='numeric') + + self.add_option('-r', '--reverse', default=False, + help='reverse the result of comparisons', + action='store_true', dest='reverse') + + self.add_option('-u', '--unique', default=False, + help='output only the first of an equal run', + action='store_true', dest='unique') + + def format_help(self, formatter=None): + raise SortError('sort [-fnru] [--ignore-case] [--numeric-sort] [--reverse] [--unique]') + + def error(self, msg): + return self.exit(msg=msg) + + def exit(self, status=0, msg=None): + if msg: + raise SortError(msg) + else: + raise SortError + +def numeric_compare(x, y): + try: a = int(x) + except: return cmp(x, y) + try: b = int(y) + except: return cmp(x, y) + return a - b + +def handle_sort(bot, ievent): + + """ sort the result list. """ + + parser = SortOptionParser() + + if not ievent.inqueue: + if not ievent.args: + ievent.missing('') + return + try: + options, result = parser.parse_args(ievent.args) + except SortError, e: + ievent.reply(str(e)) + return + else: + result = waitforqueue(ievent.inqueue, 30) + try: + options, args = parser.parse_args(ievent.rest.split()) + except SortError, e: + ievent.reply(str(e)) + return + + if not result: + ievent.reply('no data to sort') + return + + if options.unique: + result = list(set(result)) + + if options.numeric: + result.sort(numeric_compare) + else: + result.sort() + + if options.ignorecase: + result.sort(lambda a, b: cmp(a.upper(), b.upper())) + + if options.reverse: + result.reverse() + + ievent.reply(result, dot=True) + +cmnds.add('sort', handle_sort, ['USER', 'WEB', 'CLOUD'], threaded=True) +examples.add('sort', 'sort the output of a command', 'list | sort') +tests.add('list | sort') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/ignore.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/ignore.py @@ -0,0 +1,83 @@ +# plugs/ignore.py +# +# + +""" ignore users. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.utils.generic import getwho +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.ignore import addignore, delignore, ignore +from gozerbot.tests import tests +from gozerbot.users import users + +plughelp.add('ignore', 'ignore users for a certain time') + +def handle_ignore(bot, ievent): + + """ ignore nick for number of seconds. """ + + try: + (nick, nrseconds) = ievent.args + nrseconds = int(nrseconds) + except ValueError: + ievent.missing(' ') + return + + userhost = getwho(bot, nick) + + if not userhost: + ievent.reply("can't get userhost of %s" % nick) + return + + allowed = users.allowed(userhost, 'OPER', log=False) + + if allowed: + ievent.reply("can't ignore OPER") + return + + addignore(userhost, nrseconds) + ievent.reply("ignoring %s for %s seconds" % (nick, nrseconds)) + +cmnds.add('ignore', handle_ignore, ['OPER', 'IGNORE'], speed=1) +examples.add('ignore', 'ignore .. ignore for ', 'ignore test 3600') +tests.add('ignore bottest 3', 'bottest').add('ignore-del bottest') + +def handle_ignoredel(bot, ievent): + + """ remove nick from ignore list. """ + + try: + nick = ievent.args[0] + except IndexError: + ievent.missing('') + return + + userhost = getwho(bot, nick) + + if not userhost: + ievent.reply("can't get userhost of %s" % nick) + return + + if delignore(userhost): + ievent.reply("ignore for %s removed" % nick) + else: + ievent.reply("can't remove ignore of %s" % nick) + +cmnds.add('ignore-del', handle_ignoredel, ['OPER', 'IGNORE']) +examples.add('ignore-del', 'ignore-del .. unignore ', 'ignore-del dunker') +tests.add('ignore bottest 1').add('ignore-del bottest', 'removed') + +def handle_ignorelist(bot, ievent): + + """ show ignore list. """ + + ievent.reply(str(ignore)) + +cmnds.add('ignore-list', handle_ignorelist, 'OPER') +examples.add('ignore-list', 'show ignore list', 'ignore-list') +tests.add('ignore bottest 20').add('ignore-list', 'bottest').add('ignore-del bottest') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/stats.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/stats.py @@ -0,0 +1,26 @@ +# gozerbot/plugs/stats.py +# +# + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.stats import stats +from gozerbot.tests import tests + +def handle_stats(bot, ievent): + + """ show stats of the bot. """ + + if not ievent.rest: + ievent.missing('[%s]' % '|'.join(stats.all())) + return + + item = ievent.rest + result = stats.get(item) + + if result: + ievent.reply('stats of %s ==> ' % item, dict(result)) + +cmnds.add('stats', handle_stats, 'OPER') +examples.add('stats', 'show stats of the bot', '1) stats 2) stats callbacks') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/inform.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/inform.py @@ -0,0 +1,41 @@ +# gozerbot/plugs/inform.py +# +# + +""" prepend nick: to the output of a command. """ + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plugins import plugins +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +import copy + +plughelp.add('inform', 'inform the output of a command') + +def handle_inform(bot, ievent): + + """ prepend nick: to the output of command to another user """ + + try: + nick, cmnd = ievent.rest.split(' ', 1) + except ValueError: + ievent.missing(' ') + return + + event = copy.deepcopy(ievent) + event.txt = cmnd + event.onlyqueues = True + result = plugins.cmnd(bot, event) + + if not result: + ievent.reply("no result for %s" % cmnd) + return + + ievent.reply("%s: " % nick, result, dot=True) + +cmnds.add('inform', handle_inform, 'USER', threaded=True) +examples.add('inform', 'inform .. inform the output of command', 'inform dunker version') +tests.add('inform dunker version', 'dunker') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/jabber.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/jabber.py @@ -0,0 +1,99 @@ +# plugs/jabber.py +# +# + +""" jabber related commands. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot import +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.xmpp.presence import Presence + +plughelp.add('jabber', 'jabber specific commands') + +def handle_jabberratelimit(bot, ievent): + + """ set jabber output limiter. """ + + if not bot.jabber: + ievent.reply('this command only works on a jabber bot') + return + + try: + limit = int(ievent.args[0]) + except IndexError: + ievent.reply('limiter is %s seconds' % bot.state['ratelimit']) + return + except ValueError: + ievent.missing('') + return + + bot.state['ratelimit'] = limit + ievent.reply('rate limiter set to %s' % limit) + +cmnds.add('jabber-ratelimit', handle_jabberratelimit, 'OPER') +examples.add('jabber-ratelimit', 'limit jabber output', 'jabber-ratelimit 1') + +def handle_ircstr(bot, ievent): + + """ show ircevent repr like string of jabbermessage. """ + + if not bot.jabber: + ievent.reply('this command only works on a jabber bot') + return + + ievent.reply(ievent.ircstr()) + +cmnds.add('jabber-ircstr', handle_ircstr, 'OPER') +examples.add('jabber-ircstr', 'show the given event as ircstr', 'jabber-ircstr') + +def handle_show(bot, ievent): + + """ send status stanza to server """ + + if not bot.jabber: + ievent.reply('this command only works on a jabber bot') + return + + try: + bot.state['show'] = show = ievent.args[0] + except IndexError: + bot.state['show'] = show = '' + bot.state.save() + bot.sendpresence() + +cmnds.add('jabber-show', handle_show, 'OPER') +examples.add('jabber-show', 'send status to the server', 'jabber-show away') + +def handle_imhere(bot, ievent): + + """ send presence stanza to server """ + + if not bot.jabber: + ievent.reply('this command only works on a jabber bot') + return + + bot.send(Presence({'type': 'available'})) + +cmnds.add('jabber-imhere', handle_imhere, 'OPER') +examples.add('jabber-imhere', 'send presence to the server', 'jabber-imhere') + +def handle_jabberstatus(bot, ievent): + + """ set jabber status. """ + + if not bot.jabber: + ievent.reply('this command only works on a jabber bot') + return + + bot.state['status'] = ievent.rest + bot.state.save() + bot.sendpresence() + +cmnds.add('jabber-status', handle_jabberstatus, 'OPER') +examples.add('jabber-status', 'set the status of the bot', 'jabber-status happy !!') + + --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/test.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/test.py @@ -0,0 +1,250 @@ +# gozerplugs/test.py +# +# + +from gozerbot.utils.exception import exceptionmsg, handle_exception +from gozerbot.tests import tests +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.irc.ircevent import Ircevent +from gozerbot.users import users +from gozerbot.plughelp import plughelp +from gozerbot.partyline import partyline +from gozerbot.threads.thr import start_new_thread + +plughelp.add('test', 'plugin to do tests') + +import time, random + +donot = ['quit', 'reboot', 'shutdown', 'exit', 'delete', 'halt', 'upgrade', \ +'install', 'reconnect', 'wiki', 'weather', 'sc', 'jump', 'disable', 'dict', \ +'snarf', 'validate', 'popcon', 'twitter', 'tinyurl', 'whois', 'rblcheck', \ +'wowwiki', 'wikipedia', 'tr', 'translate', 'serie', 'sc', 'shoutcast', 'mash', \ +'gcalc', 'identi', 'mail', 'part', 'cycle', 'exception', 'fleet', 'rss', 'ln', 'markov-learn', 'pit', 'bugtracker', 'tu', 'banner', 'test', 'cloud', 'dispatch', 'lns'] + +def dummy(a, b=None): + return "" + +import gozerbot.utils.url +import gozerbot.generic + +oldgeturl = gozerbot.utils.url.geturl +oldgeturl2 = gozerbot.utils.url.geturl2 + +def handle_testplugs(bot, msg): + if bot.cfg['type'] == 'irc' and not msg.isdcc: + msg.reply('use this command in a /dcc chat with the bot') + return + gozerbot.utils.url.geturl = dummy + gozerbot.utils.url.geturl2 = dummy + gozerbot.generic.geturl = dummy + gozerbot.generic.geturl2 = dummy + if msg.rest: + match = msg.rest + else: + match = "" + try: + users.add('test', ['test@test',], ['USER', 'OPER']) + except Exception, ex: + pass + bot.channels.setdefault('test', {}) + try: + loop = int(msg.options['--loop']) + except (KeyError, ValueError): + loop = 1 + teller = 0 + for i in range(loop): + msg.reply('starting loop %s' % str(i)) + examplez = examples.getexamples() + random.shuffle(examplez) + for example in examplez: + if match and match not in example: + continue + skip = False + for dont in donot: + if dont in example: + skip = True + if skip: + continue + teller += 1 + if bot.jabber: + from gozerbot.xmpp.message import Message + newmessage = Message(msg) + newmessage.txt = example + newmessage.onlyqueues = False + msg.reply('command: ' + example) + try: + bot.domsg(newmessage) + except Exception, ex: + handle_exception() + else: + newmessage = Ircevent(msg) + newmessage.txt = '!' + example + newmessage.onlyqueues = False + msg.reply('command: ' + example) + try: + bot.domsg(newmessage) + except Exception, ex: + handle_exception() + try: + time.sleep(int(msg.options['--sleep'])) + except (KeyError, ValueError): + pass + msg.reply('%s tests run' % teller) + gozerbot.utils.url.geturl = oldgeturl + gozerbot.utils.url.geturl2 = oldgeturl2 + gozerbot.generic.geturl = oldgeturl + gozerbot.generic.geturl2 = oldgeturl2 + +cmnds.add('test-plugs', handle_testplugs, ['OPER', ], options={'--sleep': '0', '--loop': '1'}, threaded=True) +#tests.add('test-plugs') + +def handle_testsrun(bot, ievent): + if bot.cfg['type'] == 'irc' and not ievent.isdcc: + ievent.reply('use this command in a /dcc chat with the bot') + return + toolate = [] + err = {} + try: + loop = ievent.options['--loop'] + loop = int(loop) + except (KeyError, ValueError): + loop = 1 + tests.dotests(bot, ievent) + ievent.reply('%s tests run .. %s errors' % (tests.teller, len(tests.err))) + if tests.err: + teller = 0 + for test in tests.err: + ievent.reply('error #%s %s' % (teller, str(test))) + teller += 1 + if tests.toolate: + ievent.reply('toolate: ', tests.toolate, dot=True) + +cmnds.add('test-run', handle_testsrun, 'OPER', threaded=True, options={'--loop': '1', '--threaded': 'False'}) +examples.add('test-run', 'run the tests', 'test-run --sleep 0') + +def handle_testrunthread(bot, ievent): + if bot.cfg['type'] == 'irc' and not ievent.isdcc: + ievent.reply('use this command in a /dcc chat with the bot') + return + toolate = [] + err = {} + try: + plug = ievent.options['--plug'] + except (KeyError, ValueError): + plug = None + tests.dotests(bot, ievent, threaded=True, plug=plug) + ievent.reply('%s tests run .. %s errors' % (tests.teller, len(tests.err))) + if tests.err: + teller = 0 + for execstring, test in tests.err.iteritems(): + ievent.reply('error #%s .. %s' % (teller, str(test))) + teller += 1 + if tests.toolate: + ievent.reply('toolate: ', tests.toolate, dot=True) + +cmnds.add('test-runthread', handle_testrunthread, 'OPER', threaded=True, options={'--plug': None}) + +def handle_forcedreconnect(bot, ievent): + if bot.jabber: + bot.disconnectHandler(Exception('test exception for reconnect')) + else: + bot.sock.close() + +cmnds.add('test-forcedreconnect', handle_forcedreconnect, 'OPER') + +def handle_forcedexception(bot, ievent): + raise Exception('test exception') + +cmnds.add('test-forcedexception', handle_forcedexception, 'OPER') +#tests.add('test-forcedexception') + +def handle_testwrongxml(bot, ievent): + if not bot.jabber: + ievent.reply('only xmpp') + return + ievent.reply('sending bork xml') + bot._raw('') + +cmnds.add('test-wrongxml', handle_testwrongxml, 'OPER') + +def handle_testhammer(bot, msg): + if bot.cfg['type'] == 'irc' and not msg.isdcc: + msg.reply('use this command in a /dcc chat with the bot') + return + gozerbot.utils.url.geturl = dummy + gozerbot.utils.url.geturl2 = dummy + gozerbot.generic.geturl = dummy + gozerbot.generic.geturl2 = dummy + if msg.rest: + match = msg.rest + else: + match = "" + try: + users.add('test', ['test@test',], ['USER', 'OPER']) + except Exception, ex: + pass + bot.channels.setdefault('test', {}) + try: + loop = int(msg.options['--loop']) + except (KeyError, ValueError): + loop = 1 + try: + hammer = int(msg.options['--hammer']) + except (KeyError, ValueError): + hammer = 10 + teller = 0 + for i in range(loop): + msg.reply('starting loop %s' % str(i)) + examplez = examples.getexamples() + random.shuffle(examplez) + for example in examplez: + if match and match not in example: + continue + skip = False + for dont in donot: + if dont in example: + skip = True + if skip: + continue + teller += 1 + if bot.jabber: + from gozerbot.xmpp.message import Message + newmessage = Message(msg) + newmessage.txt = example + newmessage.onlyqueues = False + msg.reply('command: ' + example) + for hammernr in range(hammer): + try: + bot.domsg(newmessage) + except Exception, ex: + handle_exception() + else: + newmessage = Ircevent(msg) + newmessage.txt = '!' + example + newmessage.onlyqueues = False + msg.reply('command: ' + example) + for hammernr in range(hammer): + try: + bot.domsg(newmessage) + except Exception, ex: + handle_exception() + try: + time.sleep(int(msg.options['--sleep'])) + except (KeyError, ValueError): + pass + msg.reply('%s tests run' % teller) + gozerbot.utils.url.geturl = oldgeturl + gozerbot.utils.url.geturl2 = oldgeturl2 + gozerbot.generic.geturl = oldgeturl + gozerbot.generic.geturl2 = oldgeturl2 + +cmnds.add('test-hammer', handle_testhammer, 'OPER') +cmnds.add('test-hammer', handle_testhammer, 'OPER', threaded=True) +examples.add('test-hammer', 'run the hammer tests', 'test-hammer') +#tests.add('test-hammer') + +def handle_tojson(bot, ievent): + ievent.reply(ievent.tojson()) + +cmnds.add('test-json', handle_tojson, 'OPER') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/grep.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/grep.py @@ -0,0 +1,103 @@ +# plugs/grep.py +# +# + +""" grep the output of bot comamnds. """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.utils.generic import waitforqueue +from gozerbot.plughelp import plughelp +from gozerbot.examples import examples +from gozerbot.tests import tests +import getopt, re + +plughelp.add('grep', ' grep elements of a pipeline') + +def handle_grep(bot, ievent): + + """ grep the result list. """ + + if not ievent.inqueue: + ievent.reply('use grep in a pipeline') + return + + if not ievent.rest: + ievent.reply('grep ') + return + + try: + (options, rest) = getopt.getopt(ievent.args, 'riv') + except getopt.GetoptError, ex: + ievent.reply(str(ex)) + return + + result = waitforqueue(ievent.inqueue, 30) + + if not result: + ievent.reply('no data to grep on') + return + + doregex = False + docasein = False + doinvert = False + + for i, j in options: + if i == '-r': + doregex = True + if i == '-i': + docasein = True + if i == '-v': + doinvert = True + + res = [] + + if doregex: + + try: + if docasein: + reg = re.compile(' '.join(rest), re.I) + else: + reg = re.compile(' '.join(rest)) + except Exception, ex: + ievent.reply("can't compile regex: %s" % str(ex)) + return + + if doinvert: + for i in result: + if not re.search(reg, i): + res.append(i) + else: + for i in result: + if re.search(reg, i): + res.append(i) + else: + + if docasein: + what = ' '.join(rest).lower() + elif doinvert: + what = ' '.join(rest) + else: + what = ievent.rest + + for i in result: + if docasein: + if what in str(i.lower()): + res.append(i) + elif doinvert: + if what not in str(i): + res.append(i) + else: + if what in str(i): + res.append(i) + + if not res: + ievent.reply('no result') + else: + ievent.reply(res, dot=True) + +cmnds.add('grep', handle_grep, ['USER', 'WEB', 'CLOUD'], threaded=True) +examples.add('grep', 'grep the output of a command', 'list | grep core') +tests.add('list | grep core', 'core') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/nickserv.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/nickserv.py @@ -0,0 +1,272 @@ +# gozerbot/plugs/nickserv.py +# +# let gozerbot authenticate to NickServ + + +""" authenticate to NickServ. """ + +__author__ = "Wijnand 'tehmaze' Modderman - http://tehmaze.com" +__license__ ='BSD' +__gendoclast__ = ['ns-del', ] + +# gozerbot imports +from gozerbot.utils.generic import convertpickle +from gozerbot.examples import examples +from gozerbot.callbacks import callbacks +from gozerbot.commands import cmnds +from gozerbot.datadir import datadir +from gozerbot.fleet import fleet +from gozerbot.persist.pdod import Pdod +from gozerbot.plughelp import plughelp +from gozerbot.utils.log import rlog +from gozerbot.config import config +from gozerbot.tests import tests + +# basic imports +import os, time + +plughelp.add('nickserv', 'authenticate the bot through (nick)services') + +## UPGRADE PART + +def upgrade(): + + """ upgrade the nickserv data. """ + + try: + convertpickle(datadir + os.sep + 'nickserv', datadir + os.sep + 'plugs' + os.sep + \ +'nickserv' + os.sep + 'nickserv') + except Exception, ex: + rlog(10, 'nickserv', 'failed to upgrade .. %s' % str(ex)) + +## END UPGRADE PART + +class NSAuth(Pdod): + + """ nickserve auth. """ + + def __init__(self): + self.registered = False + Pdod.__init__(self, datadir + os.sep + 'plugs' + os.sep + \ +'nickserv' + os.sep + 'nickserv') + + def add(self, bot, **kwargs): + + """ add a nickserv entry. """ + + options = { + 'nickserv': 'NickServ', + 'identify': 'IDENTIFY', + } + + options.update(kwargs) + assert options.has_key('password'), 'A password must be set' + + for key in options.keys(): + Pdod.set(self, bot.name, key, options[key]) + + self.save() + + def remove(self, bot): + + """ remove a nickserv entry. """ + + if self.has_key(bot.name): + del self[bot.name] + self.save() + + def has(self, bot): + + """ check if a bot is in the nickserv list. """ + + return self.has_key(bot.name) + + def register(self, bot, passwd): + + """ register a bot to nickserv. """ + + if self.has_key(bot.name) and self.has_key2(bot.name, 'nickserv'): + bot.sendraw('PRIVMSG %s :%s %s' % (self.get(bot.name, \ +'nickserv'), 'REGISTER', passwd)) + rlog(10, 'nickserv', 'register sent on %s' % bot.server) + + def identify(self, bot): + + """ identify a bot to nickserv. """ + + #if self.has_key(bot.name) and self.has_key2(bot.name, 'nickservpass'): + #bot.say(self.get(bot.name, 'nickserv', ), '%s %s' % (self.get(bot.name, 'identify'), self.get(bot.name, 'password')), speed=9) + bot._raw("PRIVMSG %s :%s %s" % (self.get(bot.name, 'nickserv') or 'NickServ', self.get(bot.name, 'identify'), self.get(bot.name, 'password'))) + rlog(10, 'nickserv', 'identify sent on %s' % bot.server) + + def listbots(self): + + """ list all bots know. """ + + all = [] + + for bot in self.data.keys(): + all.append((bot, self.data[bot]['nickserv'])) + + return all + + def sendstring(self, bot, txt): + + """ send string to nickserver. """ + + nickservnick = self.get(bot.name, 'nickserv') + bot.say(nickservnick, txt) + + def handle_001(self, bot, ievent): + self.identify(bot) + try: + for i in self.data[bot.name]['nickservtxt']: + self.sendstring(bot, i) + rlog(10, 'nickserv', 'sent %s' % i) + except: + pass + +# basic init stuff +nsauth = NSAuth() + +if not nsauth.data: + upgrade() + nsauth = NSAuth() + +callbacks.add('001', nsauth.handle_001, threaded=True) + +def init(): + + """ init the nickserv data. """ + + passwd = config['nickservpass'] + if passwd: + nsauth.add(bot, **{'password': passwd, 'nickservtxt': config['nickservtxt']}) + return 1 + +def handle_nsadd(bot, ievent): + + """ add a bot to the nickserv. """ + + if bot.jabber: + return + + if len(ievent.args) < 1: + ievent.missing(' [] []') + return + + if nsauth.has(bot): + ievent.reply('replacing previous configuration') + + options = {} + + if len(ievent.args) >= 1: + options.update({'password': ievent.args[0]}) + + if len(ievent.args) >= 2: + options.update({'nickserv': ievent.args[1]}) + + if len(ievent.args) >= 3: + options.update({'identify': ' '.join(ievent.args[2:])}) + + nsauth.add(bot, **options) + ievent.reply('ok') + +cmnds.add('ns-add', handle_nsadd, 'OPER', threaded=True) +examples.add('ns-add', 'ns-add [] [] .. add nickserv', 'ns-add mekker') +tests.add('ns-add mekker', 'ok') + +def handle_nsdel(bot, ievent): + + """ remove a bot from nickserv. """ + + if bot.jabber: + return + + if len(ievent.args) != 1: + ievent.missing('') + return + + botname = ievent.args[0] + fbot = fleet.byname(botname) + + if not fbot: + ievent.reply('fleet bot %s not found' % botname) + return + + if not nsauth.has(fbot): + ievent.reply('nickserv not configured on %s' % fbot.name) + return + nsauth.remove(fbot) + ievent.reply('ok') + +cmnds.add('ns-del', handle_nsdel, 'OPER', threaded=True) +examples.add('ns-del', 'ns-del ', 'ns-del test') +tests.add('ns-del default', 'ok') + +def handle_nssend(bot, ievent): + + """ send string to the nickserv. """ + + if bot.jabber: + return + + if not ievent.rest: + ievent.missing('') + return + + nsauth.sendstring(bot, ievent.rest) + ievent.reply('send') + +cmnds.add('ns-send', handle_nssend, 'OPER', threaded=True) +examples.add('ns-send', 'ns-send .. send txt to nickserv', 'ns-send identify bla') +#tests.add('ns_send', 'send') + +def handle_nsauth(bot, ievent): + + """ perform an auth request. """ + + if bot.jabber: + return + + if len(ievent.args) != 1: + name = bot.name + else: + name = ievent.args[0] + + fbot = fleet.byname(name) + + if not fbot: + ievent.reply('fleet bot %s not found' % name) + return + + if not nsauth.has(fbot): + ievent.reply('nickserv not configured on %s' % fbot.name) + return + + nsauth.identify(fbot) + ievent.reply('ok') + +cmnds.add('ns-auth', handle_nsauth, 'OPER', threaded=True) +examples.add('ns-auth','ns-auth []', '1) ns-auth 2) ns-auth test') +#tests.add('ns-add mekker').add('ns-auth default', 'ok') + +def handle_nslist(bot, ievent): + + """ show a list of all bots know with nickserv. """ + + if bot.jabber: + return + + all = dict(nsauth.listbots()) + rpl = [] + + for bot in all.keys(): + rpl.append('%s: authenticating through %s' % (bot, all[bot])) + + rpl.sort() + ievent.reply(' .. '.join(rpl)) + +cmnds.add('ns-list', handle_nslist, 'OPER') +examples.add('ns-list', 'list all nickserv entries', 'ns-list') +tests.add('ns-list') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/reverse.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/reverse.py @@ -0,0 +1,33 @@ +# gozerbot/plugs/reverse.py +# +# + +__copyright__ = 'this file is in the public domain' +__author__ = 'Hans van Kranenburg ' + +# gozerbot imports +from gozerbot.utils.generic import waitforqueue +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +plughelp.add('reverse', 'reverse string or list') + +def handle_reverse(bot, ievent): + + """ reverse string or pipelined list. """ + + if ievent.inqueue: + result = waitforqueue(ievent.inqueue, 5) + elif not ievent.rest: + ievent.missing('') + return + else: + result = ievent.rest + + ievent.reply(result[::-1]) + +cmnds.add('reverse', handle_reverse, ['USER', 'CLOUD'], threaded=True) +examples.add('reverse', 'reverse text or pipeline', '1) reverse gozerbot 2) list | reverse') +tests.add('reverse gozerbot', 'tobrezog').add('list | reverse', 'misc') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/choice.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/choice.py @@ -0,0 +1,43 @@ +# plugs/choice.py +# +# + +""" the choice command can be used with a string or in a pipeline """ + +__copyright__ = 'this file is in the public domain' + +# gozerbot imports +from gozerbot.utils.generic import waitforqueue +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +# basic imports +import random + +plughelp.add('choice', 'make a random choice') + +def handle_choice(bot, ievent): + + """ make a random choice out of different words or list elements. """ + + result = [] + + if ievent.inqueue: + result = waitforqueue(ievent.inqueue, 5) + elif not ievent.args: + ievent.missing('') + return + else: + result = ievent.args + + if result: + ievent.reply(random.choice(result)) + else: + ievent.reply('nothing to choose from') + +cmnds.add('choice', handle_choice, ['USER', 'WEB', 'CLOUD'], threaded=True) +examples.add('choice', 'make a random choice', '1) choice a b c 2) list | choice') +tests.add('choice a ab ac', 'a') +tests.add('list | choice') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/userstate.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/userstate.py @@ -0,0 +1,139 @@ +# gozerbot/plugs/userstate.py +# +# + +""" userstate is stored in gozerdata/userstates. """ + +__gendoclast__ = ['userstate-del', ] + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.persist.persiststate import UserState +from gozerbot.users import users +from gozerbot.aliases import aliasset +from gozerbot.plughelp import plughelp + +plughelp.add('userstate', 'maintain state per user') + +def handle_userstate(bot, ievent): + + """ let the user manage its own state. """ + + try: + (item, value) = ievent.args + except ValueError: + item = value = None + + username = users.getname(ievent.userhost) + userstate = UserState(username) + if item and value: + userstate[item] = value + userstate.save() + + result = [] + for i, j in userstate.data.iteritems(): + result.append("%s=%s" % (i, j)) + + if result: + ievent.reply("userstate of %s: " % username, result, dot=True) + else: + ievent.reply('no userstate of %s known' % username) + +cmnds.add('userstate', handle_userstate, 'USER') +examples.add('userstate', 'get or set userstate', '1) userstate 2) userstate TZ -1') +aliasset('set', 'userstate') + +def handle_userstateget(bot, ievent): + + """ get state of a user. """ + + if not ievent.rest: + ievent.missing('') + return + + userstate = UserState(ievent.rest) + result = [] + for i, j in userstate.data.iteritems(): + result.append("%s=%s" % (i, j)) + + if result: + ievent.reply("userstate of %s: " % ievent.rest, result, dot=True) + else: + ievent.reply('no userstate of %s known' % ievent.rest) + +cmnds.add('userstate-get', handle_userstateget, 'OPER') +examples.add('userstate-get', 'get the userstate of another user', 'userstate-get dunker') + +def handle_userstateset(bot, ievent): + + """ set the userstate of a user. """ + + try: + (username, item, value) = ievent.args + except ValueError: + ievent.missing(' ') + return + + userstate = UserState(username) + userstate[item] = value + userstate.save() + ievent.reply('userstate %s set to %s' % (item, value)) + +cmnds.add('userstate-set', handle_userstateset, 'OPER') +examples.add('userstate-set', 'set userstate variable of another user', 'userstate-set dunker TZ -1') + +def handle_userstatedel(bot, ievent): + + """ remove value from user state. """ + + username = None + try: + (username, item) = ievent.args + except ValueError: + try: + item = ievent.args[0] + except IndexError: + ievent.missing('[username] ') + return + + if not username: + username = users.getname(ievent.userhost) + + userstate = UserState(username) + try: + del userstate.data[item] + except KeyError: + ievent.reply('no such item') + return + + userstate.save() + ievent.reply('item %s deleted' % item) + +cmnds.add('userstate-del', handle_userstatedel, 'OPER') +examples.add('userstate-del', 'delete userstate variable', '1) userstate-del TZ 2) userstate-del dunker TZ') + +def handle_unset(bot, ievent): + + """ remove value from user state of the user giving the command. """ + + try: + item = ievent.args[0] + except IndexError: + ievent.missing('') + return + + username = users.getname(ievent.userhost) + + userstate = UserState(username) + try: + del userstate.data[item] + except KeyError: + ievent.reply('no such item') + return + + userstate.save() + ievent.reply('item %s deleted' % item) + +cmnds.add('unset', handle_unset, 'USER') +examples.add('unset', 'delete userstate variable for user gving the command', '1) unset TZ') --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/underauth.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/underauth.py @@ -0,0 +1,41 @@ +# plugs/underauth.py +# +# + +"""Handle non-ident connection on undernet. """ + +__copyright__ = 'this file is in the public domain' +__author__ = 'aafshar@gmail.com' + +# gozerbot imports +from gozerbot.callbacks import callbacks +from gozerbot.plughelp import plughelp + +plughelp.add('underauth', 'provide callbacks that registers the bot with the \ +undernet network') + +def pre_underauth_cb(bot, ievent): + """ + Only respond to the message like: + + NOTICE AUTH :*** Your ident is disabled or broken, to continue + to connect you must type /QUOTE PASS 16188 + """ + args = ievent.arguments + + try: + return (args[0] == u'AUTH' and + args[-3] == u'/QUOTE' and + args[-2] == u'PASS') + except Exception, ex: + return 0 + +def underauth_cb(bot, ievent): + """ + Send the raw command to the server + """ + # last two elements of the arguments list are PASS + bot._raw(' '.join(ievent.arguments[-2:])) + + +callbacks.add('NOTICE', underauth_cb, pre_underauth_cb) --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/__init__.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/__init__.py @@ -0,0 +1,26 @@ +# gozerbot basic plugins +# +# + +""" register all .py files """ + +__copyright__ = 'this file is in the public domain' + +import os + +(f, tail) = os.path.split(__file__) +__all__ = [] + +for i in os.listdir(f): + if i.endswith('.py'): + __all__.append(i[:-3]) + elif os.path.isdir(f + os.sep + i): + __all__.append(i) + +try: + __all__.remove('__init__') +except ValueError: + pass + +__plugs__ = __all__ + --- gozerbot-0.99.1.orig/build/lib/gozerbot/plugs/at.py +++ gozerbot-0.99.1/build/lib/gozerbot/plugs/at.py @@ -0,0 +1,96 @@ +# plugs/at.py +# +# + +""" run a bot command at a certain time. """ + +__author__ = "Wijnand 'tehmaze' Modderman - http://tehmaze.com" +__license__ = "BSD" + +# gozerbot imports +from gozerbot.commands import cmnds +from gozerbot.examples import examples +from gozerbot.periodical import at, periodical, JobError +from gozerbot.plugins import plugins +from gozerbot.irc.ircevent import Ircevent +from gozerbot.plughelp import plughelp +from gozerbot.tests import tests + +# basic imports +import copy, types, time + +plughelp.add('at', 'schedule a command at a specific time') + +class AtJob: + + """ Job to run at certain time. """ + + def __init__(self, when, bot, nevent): + self.when = when + self.bot = bot + self.nevent = nevent + + @at(when) + def at_job(): + plugins.trydispatch(self.bot, self.nevent) + at_job.cmnd = self.nevent.txt + at_job.ievent = self.nevent + at_job() + +def handle_at(bot, ievent): + + """ start a job at a certain time. """ + + if len(ievent.args) < 2: + ievent.missing('