[Perl & C#] IRC Trivia Bot

Talk about any languages right here. Share and discuss source, but don't expect your homework to be done for you.

[Perl & C#] IRC Trivia Bot

Postby Aiden » Sat Dec 26, 2009 12:02 pm

I designed this bot so I could easily switch in and out different sets of questions depending on what I was studying for at the time. I combed a bunch of online A+ Certification tests online and grabbed their questions (and variations of those questions) and added them for the first question list. To use the list, just put it in questions/.

The Bot:
Code: Select all
#!/usr/bin/perl

# Cert Study Bot
#  by drusepth
#  on 12/24/09

use strict;
use warnings;

use IO::Socket;
use constant DEBUG => 0;
use constant OFF => 0;
use constant ON => 1;

my %_SETTINGS = (
         # Configurable
   'network'   => 'irc.tddirc.net',
   'port'      => 6667,
   'nickname'   => 'Certbot',
   'channel'   => '#A+'
);
my %_TRIVIA = (
   'chance'   => 20,   # seconds to answer question
   'wait'      => 2,   # seconds after each question before next
   'lastasked'   => 0,   # epoch time the last question was asked
   'f_asked'   => OFF,   # flag saying we're waiting on an answer
   'f_on'      => OFF,   # flag turning on/off trivia
   'f_qid'      => -1   # id of current question/answer pair
);

# Questions
our %_QUESTIONS;
do 'questions/A+.pl';

my %_SCORES;
my (@questions, @answers);

my $i = 0;
while (my ($q, $a) = each(%_QUESTIONS)) {
   $questions[$i] = $q;
   $answers[$i] = $a;
   $i++;
}

# go go go go go go go

my $sock = new IO::Socket::INET "$_SETTINGS{'network'}:$_SETTINGS{'port'}"
   or die "Cannot create connection to $_SETTINGS{'network'} on port $_SETTINGS{'port'}: $!\n";

sendPayload("USER", "$_SETTINGS{'nickname'} "x3 . ":$_SETTINGS{'nickname'}");
sendPayload("NICK", $_SETTINGS{'nickname'});

while ( my $text = <$sock>) {

   print $text if DEBUG == ON;
   chomp $text;

   my ($host, $username, $said) = ( get_host($text), get_username($text), get_said($text) );
   my @incoming = split(/ /, $text);

   if ($incoming[1] and ($incoming[1] eq "376" or $incoming[1] eq "422")) {
      sendPayload("JOIN", $_SETTINGS{'channel'});
   }

   if ($incoming[1] eq "366") {
      $_TRIVIA{'f_on'} = ON;
   }

   if ($incoming[0] eq "PING") {
      sendPayload("PONG", $incoming[1]);
      next;
   }

   # Trivia
   next unless $_TRIVIA{'f_on'} == ON;

   if ($_TRIVIA{'f_asked'} == OFF and time >= $_TRIVIA{'lastasked'} + $_TRIVIA{'wait'}) {

      # Random question
      $_TRIVIA{'f_qid'} = int rand scalar @questions;

      # Ready to ask a new question
      sendPayload("PRIVMSG", $_SETTINGS{'channel'}, ":Question:", $questions[$_TRIVIA{'f_qid'}], " >You have", $_TRIVIA{'chance'}, "seconds<");

      $_TRIVIA{'lastasked'} = time;
      $_TRIVIA{'f_asked'} = ON;

      next;
   }

   if ($_TRIVIA{'f_asked'} == ON and time < $_TRIVIA{'lastasked'} + $_TRIVIA{'chance'}) {
      # Time to answer isn't up
      sendPayload("PRIVMSG", $_SETTINGS{'channel'}, ":Received answer", $said, "from", $username) if DEBUG == ON;

      if (lc $said eq lc $answers[$_TRIVIA{'f_qid'}]) {

         $_TRIVIA{'f_asked'} = OFF;
         $_TRIVIA{'lastasked'} = time;

         if (!$_SCORES{$username}) {
            $_SCORES{$username} = 1;
         } else {
            $_SCORES{$username}++;
         }
         
         sendPayload("PRIVMSG", $_SETTINGS{'channel'}, ":$username got the answer: $answers[$_TRIVIA{'f_qid'}]! They now have $_SCORES{$username} points! Next question in", $_TRIVIA{'wait'}, "seconds.");
      }

      next;

   }

   if ($_TRIVIA{'f_asked'} == ON and time >= $_TRIVIA{'lastasked'} + $_TRIVIA{'chance'}) {
      # Answer incoming but time is up
      sendPayload("PRIVMSG", $_SETTINGS{'channel'}, ":Too late! The answer was", $answers[$_TRIVIA{'f_qid'}]);

      $_TRIVIA{'f_asked'} = OFF;
      $_TRIVIA{'lastasked'} = time;

      next;
   }

}

# Functions

sub sendPayload {

   my $p = join (' ', @_);

   print "[>] " . $p . "\n" if DEBUG == 1;
   print $sock $p . "\r\n";

}

sub get_username {
   return substr($_[0], 1, index($_[0], "!") - 1);
}

sub get_host {
   return substr($_[0], index($_[0], "!") + 1, index($_[0], " ") - index($_[0], "!") - 1);
}
sub get_said {
   my $offset = length(get_username($_[0])) + length(get_host($_[0])) + length("PRIVMSG") + length($_SETTINGS{'channel'}) + 6;
   if (length($_[0]) >= $offset) {
      my $said = substr($_[0], $offset);
      $said = substr($said, 0, length($said) - 1);
      return $said;
   } else {
      return "x";
   }
}


questions/A+.pl:
Code: Select all
# APlus Certification is offered by CompTIA ®. Undoubtedly, A+ Certification
# is the most widely recognized  certification in the field of computer hardware.
# A+ exam is targeted for computer service technicians with at least 6 months
# on-the-job experience.  To get A+ certified, one need to pass both A+ Core Hardware
# Technologies exam, 220-301  and A+ OS (Operating Systems) exam, 220-302.

# CompTIA A+ Essentials measures the necessary competencies of an entry-level IT
# professional with a recommended 500 hours of hands-on experience in the lab or
# field. It tests for the fundamentals of computer technology, networking and security,
# as well as the communication skills and professionalism now required of all
# entry-level IT professionals.

our %_QUESTIONS = (
   'What is the storage capacity of a 3 1/2" Double Sided Double Density (DSDD) Floppy disk?'
      => '720KB',
   'What is the storage capacity of a 3 1/2" Double Sided High Density (DSHD) Floppy disk?'
      => '1.44MB',
   'What is the storage capacity of a 3 1/2" Double Sided Extra Density (DSED) Floppy disk?'
      => '2.88MB',
   'When talking about floppy disks, what does DSHD stand for?'
      => 'Double Sided High Density',
   'When talking about floppy disks, what does DSDD stand for?'
      => 'Double Sided Double Density',
   'When talking about floppy disks, what does DSED stand for?'
      => 'Double Sided Extra Density',
   'How many different colors can a CGA monitor display?'
      => '16',
   'How many different colors can a EGA monitor display?'
      => '64',
   'How many different colors can a VGA monitor display?'
      => '256',
   'How many millions of different colors can a SVGA monitor display??'
      => '16',
   'What is the maximum resolution of a VGA monitor?'
      => '640x480',
   'What is the connector type that goes with CGA / EGA monitor?'
      => 'DB-9',
   'What is the connector type that goes with VGA / SVGA monitor?'
      => 'DB-15',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ0 is used with ______ ____.'
      => 'System Time',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ1 is used with the ________.'
      => 'Keyboard',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ3 is used with COM ports 2 and _.'
      => '4',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ3 is used with COM ports 4 and _.'
      => '2',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ4 is used with COM ports 1 and _.'
      => '3',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ4 is used with COM ports 3 and _.'
      => '1',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ5 is used with the parallel port ____.'
      => 'LPT2',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ6 is used with the ______ _____ controller.'
      => 'Floppy drive',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ7 is used with the parallel port ____.'
      => 'LPT1',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ8 is used with the ____ ____ _____.'
      => 'Real Time Clock',
   'There are 15 interrupt request (IRQ) numbers that correspond to locations in the computer that a bus line can be interrupted at.  IRQ12 is used with the _____.'
      => 'Mouse',
   'How many pins does a DIN-5 connector have?'
      => '5',
   'How many pins does a Mini DIN-6 connector have?'
      => '6',
   'What peripheral would you plug in with a Mini DIN-6 connector?'
      => 'Keyboard',
   'What is the port address for the serial port COM1?'
      => '3F8-3FF',
   'What is the port address for the serial port COM2?'
      => '2F8-2FF',
   'What is the port address for the serial port COM3?'
      => '3E8-3EF',
   'What is the port address for the serial port COM4?'
      => '2E8-2EF',
   'What is the port address for the parallel port LPT1?'
      => '378-37F',
   'What is the port address for the parallel port LPT2?'
      => '278-27F',
   'What is the port address for the floppy drive controller?'
      => '3F0-3F7',
   'What three-letter control signal on a serial cable indicates that the data set is ready?'
      => 'DSR',
   'How many devices can a 16-bit SCSI-2 support?'
      => '16',
   'How many devices can an 8-bit SCSI-2 support?'
      => '8',
   'You are told that a monitor at a client\'s place is having a problem. You are about to remove the cover of the monitor. A colleague tells you to wear a wrist strap to prevent electrostatic discharge (ESD) damage to the monitor. Do you follow his directions? (yes/no)'
      => 'No',
   'While booting up a client\'s computer, you receive an error message with the code 106.  You remember that POST errors 100-199 are clumped into one location of failure, so you check the ______ _____ for failures.'
      => 'System Board',
   'While booting up a client\'s computer, you receive an error message with the code 263.  You remember that POST errors 200-299 are clumped into one location of failure.  In this case, you know that location is the ______.'
      => 'Memory',
   'While booting up a client\'s computer, you receive an error message with the code 385.  You remember that POST errors 300-399 are clumped into one location of failure, so you check the ________ for failures.'
      => 'Keyboard',
   'While booting up a client\'s computer, you receive an error message with the code 406.  You remember that POST errors 400-499 are usually caused by __________ video problems.'
      => 'monochrome',
   'While booting up a client\'s computer, you receive an error message with the code 563.  You remember that POST errors 500-599 are usually caused by __________ video problems.'
      => 'color',
   'While booting up a client\'s computer, you receive an error message with the code 666.  You remember that POST errors 600-699 are clumped into one location of failure, so you check the ______ ____ for failures.'
      => 'Floppy disk',
   'While booting up a client\'s computer, you receive an error message with the code 1723.  You remember that POST errors 1700-1799 are clumped into one location of failure, so you check the ______ ____ for failures.'
      => 'Hard disk',
   'True or False: An MCA bus supports PnP, bus mastering, & burst mode.'
      => 'False',
   'True or False: An MCA bus supports PnP and bus mastering, but not burst mode.'
      => 'True',
   'True or False: A PCI bus supports PnP, bus mastering, & burst mode.'
      => 'True',
   'Of the following, which is NOT a type of motherboard expansion slot?  (ISA / PCI / AGP / ATX)'
      => 'ATX',
   'Which IRQ does COM1 commonly use?'
      => '4',
   'Of the following, which is NOT a type of computer hard drive?  (IDE / EIDE / SCSI / FDD)'
      => 'FDD',
   'Which of the following retains the information it\'s storing when the power to the system is turned off? (CPU / RAM / ROM / DRAM / DIMM)'
      => 'ROM',
   'Does a computer\'s RAM retain the information it\'s storing when the power to the system is turned off? (Yes/No)'
      => 'No',
   'Does a computer\'s ROM retain the information it\'s storing when the power to the system is turned off? (Yes/No)'
      => 'Yes',
   'Does a computer\'s CPU retain the information it\'s storing when the power to the system is turned off? (Yes/No)'
      => 'No',
   'Many ribbon cables have a red line along one edge that indicates the side of the ribbon that should be aligned with this pin on the matching connector.'
      => '1',
   'Which IRQ does the system timer commonly use?'
      => '0',
   'What is the BIOS an acrynym for?'
      => 'Basic Input Output System',
   'Which of the following is NOT a kind of RAM? (DIMM / SIMM / ROM / SLIPP)'
      => 'ROM',
   'When working on a computer taken into you by a client, you determine that there are bad sectors on the hard drive. A colleague suggests that you run the FDISK command to fix them. Will this help? (Yes/No)'
      => 'No',
   'True or false: the FDISK command is used to create partitions on a hard drive.'
      => 'True',
   'What type of connector is used to plug a telephone line into a modem?'
      => 'RJ-11',
   'Which IRQ does LPT1 commonly use?'
      => '7',
   'You\'re observing a colleague plug in the P8 and P9 power connectors in a system board. You notice that the black wires of both connectors are right next to each other. Did your colleague make a mistake? (Yes/No)'
      => 'No',
   'A SIMM can either have 72 or __ pins.'
      => '30',
   'A SIMM can either have 30 or __ pins.'
      => '72',
   'How many pins does a DIMM have?'
      => '168',
   'True or false: parity is a system for balancing the voltage coming out of the power supply. (True/False)'
      => 'False',
   'True or false: parity is an extra bit stored with data in RAM that is used to check for errors when the data is read back. (True/False)'
      => 'True',
   'You\'re cleaning a client\'s PC and you notice the power supply is exceptionally dirty. Your colleague suggests using compressed air. Do you follow his advice? (Yes/No)'
      => 'Yes',
   'You\'ve played nothing but flash games online for the last six months. These flash games haven\'t required keyboard input, and so your keyboard has gathered a fair amount of dust. You think the best way to clean it would be to just remove the bezel and blow out the dust. Are you right? (Yes/No)'
      => 'Yes',
   'What kind of connectors are used to connect a PC power supply to a hard drive?'
      => 'Molex',
   'IRQ stands for ________ _______.'
      => 'Interrupt Request',
   'Two crazy software engineers are dismantling computers in two rooms. One room is 50 degrees Fahrenheit, and the other is 80 degrees Fahrenheit. Both rooms are moderately dry. Without any precautions, which engineer has a greater chance of encountering electrostatic discharge? (Hot/Cold)'
      => 'Cold',
   'Which of the following file types can not be ran simply by typing them at the DOS prompt? (com / bat / dat / exe)'
      => 'dat',
   'A cluster is the minimum file allocation unit. A cluster is composed of these. (RAM / ROM / sectors / sections / clustettes / bytes)'
      => 'sectors',
   
);
"When it takes forever to learn all the rules, no time is left for breaking them."
User avatar
Aiden
Administrator
 
Posts: 1079
Joined: Tue Oct 31, 2006 11:11 pm
Location: /usr/bin/perl

Re: [Perl] IRC Trivia Bot

Postby infinite_ » Sun Jan 10, 2010 11:34 am

Heh, that's a cool idea for studying.
My effort to help you will never exceed your effort to explain the problem.
User avatar
infinite_
Bat Country
 
Posts: 1353
Joined: Fri Jun 04, 2004 7:19 pm
Location: Australia

Re: [Perl] IRC Trivia Bot

Postby Aiden » Sun Jan 10, 2010 4:27 pm

With classes about to start up, I'm going to try to populate a list for each of my classes and see how that works for reviewing and studying for tests and such. :)
"When it takes forever to learn all the rules, no time is left for breaking them."
User avatar
Aiden
Administrator
 
Posts: 1079
Joined: Tue Oct 31, 2006 11:11 pm
Location: /usr/bin/perl

Re: [Perl] IRC Trivia Bot

Postby Aiden » Sun Jan 17, 2010 3:42 pm

*cough* *cough* C# *cough*

Code: Select all
/*
 * StudyBot
 * Latest release as of: 01/17/09
 *
 * by Andrew Brown
 * http://www.drusepth.net/
 *
 * This bot is meant to be an easily configurable spin-off of the generic "trivia"
 * bots, in that it keeps a (currently flat) database of questions from each of your
 * classes and relays those questions over IRC, where you (or a channel of people)
 * can compete to answer them.
 *
 * The questions are stored in a text file defined by the variable questionsFile in
 * the following format:
 *      SUBJECT;QUESTION;ANSWER
 *
 * As people correctly answer questions, they will earn points on the scoreboard, which
 * is currently stored in a plaintext file defined by the variable scoreboardFile in the
 * format of:
 *      USERNAME:SCORE
 * Note that the scoreboard uses a colon (:) whereas the questions file uses a semicolon.
 * It's more likely that a question will contain a colon than a semicolon, and a colon
 * seems to be the logical separator for the scoreboard format.
 *
 * TODO:
 *      - Move the flat data sources to a database. Although this will slightly hinder
 *        portability, it will remedy the inherit problems of delimited files, such as
 *        when questions include the delimiter.
 *       
 *      - Keep track of which questions each person has answered correctly, and ask the
 *        "easier" questions less often. The point of this bot is to learn and review
 *        material, and asking the harder questions more frequently will no doubt help.
 *       
 *      - Make the trivia asynchronous. Currently, it pauses at the top of the main loop
 *        to wait for data from the server, and that means it can't be checking if a
 *        question's time is up or if a new question needs asked.
 *
 */

using System;
using System.IO;
using System.Net.Sockets;
using System.Collections.Generic;

namespace StudyBot
{
    class Program
    {
        static void Main(string[] args)
        {
            // Configurable bot vars
            string nickname = "StudyBot";
            string server = "irc.tddirc.net";
            int port = 6667;
            string channel = "#alone";
            string questionsFile = "Questions.txt";
            string scoreboardFile = "Scoreboard.txt";

            // Configurable study vars
            int questionTime = 10; // seconds
            int cooldownTime = 2; // seconds between questions

            // Other study vars
            bool studyMode = false;
            bool askedQuestion = false;
            int lastAction = GetUnixEpoch(DateTime.Now);

            // Socket vars
            StreamWriter swrite;
            StreamReader sread, fread;
            NetworkStream sstream;
            TcpClient irc;

            // Generic bot vars
            bool botOn = true;
            string line; // incoming line
            string[] splitLine; // array of line, expoded by \s
            Random rng = new Random();

            // Study questions
            List<string> topics = new List<string>();
            List<string> questions = new List<string>();
            List<string> answers = new List<string>();
            int currentQID = 0;

            // Scoreboard
            List<string> scoreboard_user = new List<string>();
            List<int> scoreboard_score = new List<int>();
            int pointsSinceUpdate = 0;

            // Read in questions from a text file
            if (File.Exists(questionsFile))
            {
                fread = new StreamReader(questionsFile);

                while (!fread.EndOfStream) {
                    splitLine = fread.ReadLine().Split(';');

                    topics.Add(splitLine[0]);
                    questions.Add(splitLine[1]);
                    answers.Add(splitLine[2]);
                }

                fread.Close();

            }
            else
            {
                Console.WriteLine("Cannot find any questions at {0}!", questionsFile);
                return;
            }

            // Read in scoreboard from a text file
            if (File.Exists(scoreboardFile))
            {
                fread = new StreamReader(scoreboardFile);

                while (!fread.EndOfStream) {
               
                    splitLine = fread.ReadLine().Split(':');

                    scoreboard_user.Add(splitLine[0]);
                    scoreboard_score.Add(Int32.Parse(splitLine[1]));
                }
            }
            else
            {
                Console.WriteLine("Cannot find an existing scoreboard at {0}; making a new one.", scoreboardFile);
                File.Create(scoreboardFile);
            }

            try
            {
                // Connect
                irc = new TcpClient(server, port);
                sstream = irc.GetStream();
                sread = new StreamReader(sstream);
                swrite = new StreamWriter(sstream);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error connecting to {0}: {1}", server, e);
                return;
            }
           
            // Identify
            swrite.WriteLine("USER {0} {0} {0} :{1}", nickname, nickname);
            swrite.Flush();
            swrite.WriteLine("NICK {0}", nickname);
            swrite.Flush();

            while (botOn)
            {

                if ((line = sread.ReadLine()) != null) {

                    Console.WriteLine(line);

                    splitLine = line.Split(' ');

                    if (splitLine.Length > 0)
                    {
                        switch (splitLine[1])
                        {
                            case "366":
                                studyMode = true;
                                System.Threading.Thread.Sleep(cooldownTime * 1000);

                                break;

                            case "376":
                            case "422":
                                swrite.WriteLine("JOIN {0}", channel);
                                swrite.Flush();
                                break;
                        }

                        if (splitLine[0] == "PING")
                        {
                            swrite.WriteLine("PONG {0}", splitLine[1]);
                            swrite.Flush();
                        }
                    }

                    if (studyMode)
                    {
                        if (askedQuestion)
                        {
                            if (GetUnixEpoch(DateTime.Now) >= lastAction + questionTime)
                            {
                                // Time is up!
                                swrite.WriteLine("PRIVMSG {0} :Time's up! The answer was {1}!",
                                    channel,
                                    answers[currentQID]);
                                swrite.Flush();

                                askedQuestion = false;
                            }
                            else
                            {
                                string user = GetUsernameSpeaking(line);
                                string answer = GetSpokenLine(line);

                                // Check if answer is right
                                if (answers[currentQID].ToLower() == answer.ToLower())
                                {
                                    // Correct
                                    int uid = -1;

                                    for (int i = 0; i < scoreboard_user.Count; i++)
                                    {
                                        if (user == scoreboard_user[i])
                                            uid = i;
                                    }

                                    if (uid == -1)
                                    {
                                        scoreboard_user.Add(user);
                                        scoreboard_score.Add(1);

                                        uid = scoreboard_user.Count - 1; // starts counting at 1
                                    }
                                    else
                                    {
                                        scoreboard_score[uid]++;
                                        pointsSinceUpdate++;

                                        if (pointsSinceUpdate == 5)
                                        {
                                            // Update

                                            pointsSinceUpdate = 0;
                                        }
                                    }

                                    swrite.WriteLine("PRIVMSG {0} :{1} got it right with their answer: {2}! They now have {3} points!",
                                        channel,
                                        user,
                                        answer,
                                        scoreboard_score[uid]);
                                    swrite.Flush();

                                    askedQuestion = false;

                                    System.Threading.Thread.Sleep(cooldownTime * 1000);

                                }

                            }

                        }
                        else
                        {
                            // Ask a question
                            currentQID = rng.Next(questions.Count);

                            swrite.WriteLine("PRIVMSG {0} :{1} question: {2} (You have {3} seconds.)",
                                channel,
                                topics[currentQID],
                                questions[currentQID],
                                questionTime);
                            swrite.Flush();

                            askedQuestion = true;
                            lastAction = GetUnixEpoch(DateTime.Now);
                        }
                    }

                }

            }

            // Clean up
            swrite.Close();
            sread.Close();
            irc.Close();

        }

        private static int GetUnixEpoch(DateTime dateTime)
        {
            var unixTime = dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

            return (int)unixTime.TotalSeconds;
        }

        private static string GetUsernameSpeaking(string line)
        {
            // :drusepth!drusepth@google.gov PRIVMSG #hackerthreads :Welcome to #hackerthreads I love you
            return line.Split('!')[0].Split(':')[1];
        }

        private static string GetHostSpeaking(string line)
        {
            return line.Split('!')[1].Split(' ')[0];
        }

        private static string GetSpokenLine(string line)
        {
            if (line.Split(':').Length >= 2)
                return line.Split(':')[2];

            return "";
        }
    }
}
"When it takes forever to learn all the rules, no time is left for breaking them."
User avatar
Aiden
Administrator
 
Posts: 1079
Joined: Tue Oct 31, 2006 11:11 pm
Location: /usr/bin/perl


Return to Programming

Who is online

Users browsing this forum: No registered users and 0 guests