changeset 656:79e785f0bdad

For issue #38, change IRC bot to use Redis instead of MySQL. Also deleting the bot from this repo as it now has its own repo.
author Brian Neal <bgneal@gmail.com>
date Sat, 11 May 2013 15:22:45 -0500
parents d9d6b4b8bab7
children 6467eabe74d4
files irc/channel.py irc/models.py irc/templatetags/irc_tags.py irc/views.py sg101/templates/irc/irc_block.html sg101/templates/irc/view.html tools/sg101Bot.py
diffstat 7 files changed, 29 insertions(+), 337 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irc/channel.py	Sat May 11 15:22:45 2013 -0500
@@ -0,0 +1,15 @@
+"""Abstracts the list of users in the IRC channel."""
+
+from redis import RedisError
+
+from core.services import get_redis_connection
+
+def get_users():
+    """Return a list of users in the IRC channel."""
+    conn = get_redis_connection()
+    try:
+        nicks = conn.get('irc:channel_members')
+    except RedisError:
+        nicks = None
+
+    return nicks.split() if nicks else []
--- a/irc/models.py	Sat May 11 12:56:41 2013 -0500
+++ b/irc/models.py	Sat May 11 15:22:45 2013 -0500
@@ -1,15 +1,1 @@
-"""Models for the IRC application. 
-The IRC application simply reports who is in the site's IRC chatroom. A bot in the channel updates
-the table and we read it.
-"""
-from django.db import models
-
-class IrcChannel(models.Model):
-   name = models.CharField(max_length=30)
-   last_update = models.DateTimeField()
-
-   def __unicode__(self):
-      return self.name
-
-   class Meta:
-      ordering = ('name', )
+"""Models for the IRC application."""
--- a/irc/templatetags/irc_tags.py	Sat May 11 12:56:41 2013 -0500
+++ b/irc/templatetags/irc_tags.py	Sat May 11 15:22:45 2013 -0500
@@ -1,14 +1,13 @@
-"""
-Template tags for the IRC application.
-"""
+"""Template tags for the IRC application."""
+
 from django import template
-from irc.models import IrcChannel
+
+from irc.channel import get_users
 
 register = template.Library()
 
 @register.inclusion_tag('irc/irc_block.html')
 def irc_status():
-    nicks = IrcChannel.objects.all()
     return {
-        'nicks': nicks,
+        'nicks': get_users(),
     }
--- a/irc/views.py	Sat May 11 12:56:41 2013 -0500
+++ b/irc/views.py	Sat May 11 15:22:45 2013 -0500
@@ -1,12 +1,10 @@
-"""views for the IRC application"""
+"""Views for the IRC application"""
 
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
 
-from irc.models import IrcChannel
+from irc.channel import get_users
+
 
 def view(request):
-   nicks = IrcChannel.objects.all()
-   return render_to_response('irc/view.html',
-         {'nicks': nicks},
-         context_instance = RequestContext(request))
+   nicks = get_users()
+   return render(request, 'irc/view.html', {'nicks': nicks})
--- a/sg101/templates/irc/irc_block.html	Sat May 11 12:56:41 2013 -0500
+++ b/sg101/templates/irc/irc_block.html	Sat May 11 15:22:45 2013 -0500
@@ -4,7 +4,7 @@
 {% if nicks %}
 <ul>
    {% for nick in nicks %}
-      <li>{{ nick.name }}</li>
+      <li>{{ nick }}</li>
    {% endfor %}
 </ul>
 <p class="centered">Join them in the <a href="irc://surfguitar101.com/ShallowEnd,needpass">#ShallowEnd</a>!</p>
--- a/sg101/templates/irc/view.html	Sat May 11 12:56:41 2013 -0500
+++ b/sg101/templates/irc/view.html	Sat May 11 15:22:45 2013 -0500
@@ -5,7 +5,7 @@
 {% if nicks %}
 <ul>
    {% for nick in nicks %}
-      <li>{{ nick.name }}</li>
+      <li>{{ nick }}</li>
    {% endfor %}
 </ul>
 <p>Join them in the <a href="irc://surfguitar101.com/ShallowEnd,needpass">#ShallowEnd</a>!</p>
--- a/tools/sg101Bot.py	Sat May 11 12:56:41 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,306 +0,0 @@
-#! /usr/bin/env python
-"""sg101Bot.py
-
-IRC Bot for SurfGuitar101.com's IRC server.
-This bot watches who is in the given channel and updates a MySQL
-database accordingly. The database is read by the website to display
-who is in the channel.
-
-Brian Neal <brian@surfguitar101.com>
-"""
-
-import re
-import sys
-import random
-import logging
-from optparse import OptionParser
-import ConfigParser
-
-import MySQLdb
-import irclib
-from daemon import Daemon
-
-#irclib.DEBUG = True
-
-class QuoteDb(object):
-   """QuoteDb is a class that reads a quote file and provdes a random quote when asked"""
-
-   def __init__(self, filename):
-      random.seed()
-      f = open(filename, 'r')
-      self.quotes = []
-      for line in f:
-         self.quotes.append(line.strip())
-      f.close()
-
-   def get(self):
-      return random.choice(self.quotes)
-      
-
-class Sg101Table(object):
-   """Sg101Table class for abstracting the database table that is used to store who is in the channel"""
-   
-   def __init__(self, dbHost, dbName, dbUser, dbPassword, tableName):
-      self.dbArgs = {'host' : dbHost, 'user' : dbUser, 'passwd' : dbPassword, 'db' : dbName}
-      self.table = tableName
-      self.dbRefCnt = 0
-
-   def empty(self):
-      self.__dbOpen()
-      """empties the table of channel users"""
-      logging.debug('emptying the table')
-      self.cursor.execute('TRUNCATE TABLE ' + self.table)
-      self.__dbClose()
-
-   def add(self, nick):
-      self.__dbOpen()
-      """adds the nickname to the table"""
-      logging.debug('adding to the table: ' + nick)
-      self.cursor.execute("INSERT INTO " + self.table +
-          " (name, last_update) VALUES (%s, NOW())", nick)
-      self.__dbClose()
-
-   def remove(self, nick):
-      self.__dbOpen()
-      """removes the nickname from the table"""
-      logging.debug('removing from the table: ' + nick)
-      self.cursor.execute("DELETE FROM " + self.table + " WHERE `name` = %s", nick)
-      self.__dbClose()
-
-   def set(self, nicks):
-      self.__dbOpen()
-      """empties and then adds all the nicknames in the nicks list to the table"""
-      self.empty()
-      logging.debug('setting the table: ' + ', '.join(nicks))
-      for nick in nicks:
-         self.add(nick)
-      self.__dbClose()
-
-   def __dbOpen(self):
-      if self.dbRefCnt <= 0:
-         self.connection = MySQLdb.connect(
-            host = self.dbArgs['host'],
-            user = self.dbArgs['user'],
-            passwd = self.dbArgs['passwd'],
-            db = self.dbArgs['db'])
-         self.cursor = self.connection.cursor()
-         self.dbRefCnt = 1
-      else:
-         self.dbRefCnt = self.dbRefCnt + 1
-
-   def __dbClose(self):
-      self.dbRefCnt = self.dbRefCnt - 1
-      if self.dbRefCnt <= 0:
-         self.cursor.close()
-         self.connection.commit()
-         self.connection.close()
-         self.dbRefCnt = 0
-      
-
-class Sg101Bot(irclib.SimpleIRCClient):
-   """Sg101Bot class for monitoring a given channel and updating a database table accordingly"""
-
-   def __init__(self, channel, nickname, server, port, password, db, quotes, quoteMod = 5):
-      """__init__ function for Sg101Bot"""
-
-      irclib.SimpleIRCClient.__init__(self)
-      self.channel = channel
-      self.nickname = nickname
-      self.server = server
-      self.port = port
-      self.password = password
-      self.users = []
-      self.db = db
-      self.quotes = quotes
-      self.quoteMod = quoteMod
-      self.pingCount = 0;
-
-   def start(self):
-      """call this function to start the bot"""
-      self.db.empty()
-      self.__connect()
-      irclib.SimpleIRCClient.start(self)
-
-   def on_nicknameinuse(self, c, e):
-      """called if nick is in use; attempts to change nick"""
-      c.nick(c.get_nickname() + "_")
-
-   def on_welcome(self, c, e):
-      """called when welcomed to the IRC server; attempts to join channel"""
-      c.join(self.channel)
-
-   def on_namreply(self, c, e):
-      """called when we hear a namreply; we scan the names and if different than our
-      info of who is in the channel we update the database"""
-      me = c.get_nickname()
-      users = [re.sub('^[@\+]', '', u) for u in e.arguments()[2].split(' ') if u != me]
-      users.sort()
-      if (self.users != users):
-         logging.debug('on_namreply: detecting user list difference')
-         self.users = users
-         self.db.set(users)
-         self.__printUsers()
-
-   def on_join(self, c, e):
-      """called after we or someone else joins the channel; update our list of users and db"""
-      nick = irclib.nm_to_n(e.source())
-      if nick != c.get_nickname() and nick not in self.users:
-         self.__addUser(nick)
-         self.__printUsers()
-
-   def on_part(self, c, e):
-      """called after we or someone else parts the channel; update our list of users and db"""
-      nick = irclib.nm_to_n(e.source())
-      if nick != c.get_nickname():
-         self.__delUser(nick)
-         self.__printUsers()
-
-   def on_kick(self, c, e):
-      """called after someone is kicked; update our list of users and db"""
-      nick = e.arguments()[0]
-      if nick != c.get_nickname():
-         self.__delUser(nick)
-         self.__printUsers()
-
-   def on_quit(self, c, e):
-      """called after someone quits; update our list of users and db"""
-      nick = irclib.nm_to_n(e.source())
-      if nick != c.get_nickname():
-         self.__delUser(nick)
-         self.__printUsers()
-
-   def on_ping(self, c, e):
-      """called when we are pinged; we use this opportunity to ask who is in the channel via NAMES"""
-      c.names()
-      self.pingCount = self.pingCount + 1
-      if (self.quoteMod > 0) and len(self.users) > 0 and (self.pingCount % self.quoteMod) == 0:
-         c.privmsg(self.channel, self.quotes.get())
-
-   def on_privmsg(self, c, e):
-      self.doCommand(e, e.arguments()[0], e.source())
-
-   def on_pubmsg(self, c, e):
-      a = e.arguments()[0].split(':', 1)
-      if len(a) > 1 and irclib.irc_lower(a[0]) == irclib.irc_lower(c.get_nickname()):
-         self.doCommand(e, a[1].strip(), e.target())
-
-   def on_disconnect(self, c, e):
-      logging.critical('received on_disconnect()')
-      logging.debug('scheduling a routine in 30 secs')
-      self.users = []
-      self.db.empty()
-      self.connection.execute_delayed(30, self.__connectedChecker)
-
-   def __connectedChecker():
-      logging.debug('__connectedChecker()')
-      if not self.connection.is_connected():
-         logging.debug('rescheduling __connectedChecker in 30 secs')
-         self.connection.execute_delayed(30, self._connected_checker)
-         self.__connect()
-
-
-   def doCommand(self, e, cmd, reply):
-      if cmd == 'yow':
-         self.connection.privmsg(reply, self.quotes.get())
-
-   def __connect(self):
-      """our internal connect function"""
-      self.connect(self.server,
-                   self.port,
-                   self.nickname,
-                   self.password)
-
-   def __printUsers(self):
-      """internal print users command"""
-      msg = 'My users are: (' + ', '.join(self.users) +  ')'
-      logging.debug(msg)
-
-   def __addUser(self, nick):
-      """adds a user to our list and db"""
-      self.users.append(nick)
-      self.users.sort()
-      self.db.add(nick)
-
-   def __delUser(self, nick):
-      """removes a user from our list and db"""
-      if nick in self.users:
-         self.users.remove(nick)
-      self.db.remove(nick)
-
-
-class BotDaemon(Daemon):
-    def __init__(self, filename, options, db_config):
-        Daemon.__init__(self, filename)
-        self.options = options
-        self.db_config = db_config
-
-    def run(self):
-        logging.basicConfig(level = logging.DEBUG,
-                           format = '%(asctime)s %(levelname)s %(message)s',
-                           filename = '/home/brian/irc/sg101Bot.log',
-                           filemode = 'w')
-        logging.info('sg101Bot starting')
-
-        server = self.options.server
-        port = self.options.port
-        channel = self.options.channel
-        nickname = self.options.nick
-        password = self.options.password
-
-        try:
-          quotes = QuoteDb('/home/brian/irc/zippy.txt')
-          db = Sg101Table(self.db_config['host'],
-                  self.db_config['database'],
-                  self.db_config['user'],
-                  self.db_config['password'],
-                  self.db_config['table'])
-          bot = Sg101Bot(channel, nickname, server, port, password, db, quotes, 0)
-          bot.start()
-        except MySQLdb.OperationalError, message:
-          logging.exception('MySQLdb.OperationalError: %d %s' % (message[0], message[1]))
-        except MySQLdb.ProgrammingError, message:
-          logging.exception('MySQLdb.ProgrammingError: %d %s' % (message[0], message[1]))
-        except:
-           print "Got an exception:", sys.exc_info()[0]
-           logging.exception('Exception received ' + str(sys.exc_info()))
-
-        logging.info('sg101Bot ending')
-
-if __name__ == "__main__":
-    parser = OptionParser(usage='usage: %prog [options] start|stop|restart',
-         description="""\
-    SG101 IRC Bot. Monitors who is in the specified channel and
-    updates a database table accordingly for display by the website.
-    """)
-
-    parser.add_option("-s", "--server", default="localhost",
-         help="server address")
-    parser.add_option("-p", "--port", type="int", default=6667,
-         help="port number")
-    parser.add_option("-c", "--channel", default="#ShallowEnd",
-         help="channel name")
-    parser.add_option("-n", "--nick", default="bot",
-         help="bot nickname")
-    parser.add_option("-w", "--password", default="morereverb",
-         help="channel password")
-    parser.add_option("-q", "--quote", default="/home/brian/irc/zippy.txt",
-         help="Quote file")
-    (options, args) = parser.parse_args()
-
-    commands = ('start', 'stop', 'restart')
-    if len(args) != 1 and args[0] not in commands:
-        parser.print_help()
-        sys.exit("Please provide a single command: start, stop, or restart.")
-
-    config = ConfigParser.ConfigParser()
-    config.read('/home/brian/irc/sg101Bot.ini')
-    db_config = dict(config.items('Database'))
-
-    daemon = BotDaemon('/tmp/sg101Bot.pid', options, db_config)
-    cmd = args[0]
-    if cmd == 'start':
-        daemon.start()
-    elif cmd == 'stop':
-        daemon.stop()
-    elif cmd == 'restart':
-        daemon.restart()