2009-10-05 14:33:30 -06:00
import badmath
import time
import os
import traceback
import pickle
from hashlib import sha256
2009-10-05 15:30:37 -06:00
try :
from ctf import irc
from ctf . flagd import Flagger
except :
import sys
sys . path . append ( ' /home/pflarr/repos/gctf/ ' )
2009-10-05 15:43:24 -06:00
from ctf . flagd import Flagger
from ctf import irc
2009-10-05 14:33:30 -06:00
class Gyopi ( irc . Bot ) :
2009-10-05 15:43:24 -06:00
STATE_FN = ' badmath.state '
2009-10-05 14:33:30 -06:00
SALT = b ' this is questionable. '
FLAG_DEFAULT = ' dirtbags '
MAX_ATTEMPT_RATE = 3
NOBODY = ' \002 [nobody] \002 '
FLAG_HOST = b ' ctf1.lanl.gov '
# FLAG_HOST = b'localhost'
2009-10-05 15:30:37 -06:00
def __init__ ( self , host , channels , dataPath , flagger ) :
irc . Bot . __init__ ( self , host , [ ' gyopi ' ] , ' Gyopi ' , channels )
2009-10-05 14:33:30 -06:00
self . _dataPath = dataPath
self . _flag = flagger
try :
self . _loadState ( )
except :
traceback . print_exc ( )
self . _lvl = 0
self . _flag . set_flag ( self . FLAG_DEFAULT )
2009-10-07 10:50:18 -06:00
self . _tokens = [ ]
2009-10-05 14:33:30 -06:00
self . _lastAttempt = { }
self . _affiliations = { }
self . _newPuzzle ( )
def err ( self , exception ) :
""" Save the traceback for later inspection """
irc . Bot . err ( self , exception )
t , v , tb = exception
info = [ ]
while 1 :
info . append ( ' %s : %d ( %s ) ' %
( os . path . basename ( tb . tb_frame . f_code . co_filename ) ,
tb . tb_lineno ,
tb . tb_frame . f_code . co_name ) )
tb = tb . tb_next
if not tb :
break
del tb # just to be safe
infostr = ' [ ' + ' ] [ ' . join ( info ) + ' ] '
self . last_tb = ' %s %s %s ' % ( t , v , infostr )
print ( self . last_tb )
2009-10-07 10:50:18 -06:00
def cmd_JOIN ( self , sender , forum , addl ) :
2009-10-05 14:33:30 -06:00
""" On join, announce who has the flag. """
if sender . name ( ) in self . nicks :
self . _tellFlag ( forum )
self . _tellPuzzle ( forum )
def _newPuzzle ( self ) :
""" Create a new puzzle. """
self . _key , self . _puzzle , self . _banned = badmath . mkPuzzle ( self . _lvl )
def _loadState ( self ) :
""" Load the last state from the stateFile. """
statePath = os . path . join ( self . _dataPath , self . STATE_FN )
stateFile = open ( statePath , ' br ' )
state = pickle . load ( stateFile )
self . _lvl = state [ ' lvl ' ]
self . _flag . set_flag ( state [ ' flag ' ] )
self . _lastAttempt = state [ ' lastAttempt ' ]
self . _affiliations = state [ ' affiliations ' ]
self . _puzzle = state [ ' puzzle ' ]
self . _key = state [ ' key ' ]
self . _banned = state [ ' banned ' ]
self . _tokens = state . get ( ' tokens ' , [ ] )
def _saveState ( self ) :
""" Write the current state to file. """
state = { ' lvl ' : self . _lvl ,
' flag ' : self . _flag . flag ,
' lastAttempt ' : self . _lastAttempt ,
' affiliations ' : self . _affiliations ,
' puzzle ' : self . _puzzle ,
' key ' : self . _key ,
' banned ' : self . _banned ,
' tokens ' : self . _tokens }
# Do the write as an atomic move operation
statePath = os . path . join ( self . _dataPath , self . STATE_FN )
stateFile = open ( statePath + ' .tmp ' , ' wb ' )
pickle . dump ( state , stateFile )
stateFile . close ( )
2009-10-05 15:30:37 -06:00
os . rename ( statePath + ' .tmp ' , statePath )
2009-10-05 14:33:30 -06:00
def _tellFlag ( self , forum ) :
""" Announce who owns the flag. """
forum . msg ( ' %s has the flag. ' % ( self . _flag . flag ) )
def _tellPuzzle ( self , forum ) :
""" Announce the current puzzle. """
2009-10-05 15:30:37 -06:00
forum . msg ( ' Difficulty level is %d ' % self . _lvl )
2009-10-05 14:33:30 -06:00
forum . msg ( ' The problem is: %s ' % ' ' . join ( map ( str , self . _puzzle ) ) )
def _getStations ( self ) :
stations = { }
with open ( os . path . join ( STORAGE , ' stations.txt ' ) ) as file :
lines = file . readlines ( )
for line in lines :
try :
name , file = line . split ( ' : ' )
except :
continue
stations [ name ] = file
return stations
def _giveToken ( self , user , forum ) :
""" Hand a Jukebox token to the user. """
token = self . _jukebox . mkToken ( user )
forum . msg ( ' You get a jukebox token: %s ' % token )
forum . msg ( ' Use this with the !set command to change the music. ' )
forum . msg ( ' This token is specific to your user name, and is only '
' useable once. ' )
def _useToken ( self , user , forum , token , station ) :
""" Use the given token, and change the current station to station. """
try :
station = int ( station )
stations = self . _getStations ( )
assert station in stations
except :
forum . msg ( ' %s : Invalid Station ( %s ) ' % station )
return
if token in self . _tokens [ user ] :
self . _tokens [ user ] . remove ( token )
2009-10-07 10:50:18 -06:00
def cmd_PRIVMSG ( self , sender , forum , addl ) :
2009-10-05 14:33:30 -06:00
text = addl [ 0 ]
who = sender . name ( )
if text . startswith ( ' ! ' ) :
2009-10-07 10:50:18 -06:00
parts = text [ 1 : ] . split ( ' ' , 1 )
2009-10-05 14:33:30 -06:00
cmd = parts [ 0 ]
if len ( parts ) > 1 :
args = parts [ 1 ]
else :
args = None
if cmd . startswith ( ' r ' ) :
# Register
if args :
self . _affiliations [ who ] = args
team = self . _affiliations . get ( who , self . NOBODY )
forum . msg ( ' %s is playing for %s ' % ( who , team ) )
elif cmd . startswith ( ' w ' ) :
forum . msg ( ' Teams: ' )
for player in self . _affiliations :
forum . msg ( ' %s : %s ' % ( player , self . _affiliations [ player ] ) )
elif cmd . startswith ( ' embrace ' ) :
# Embrace
forum . ctcp ( ' ACTION ' , ' is devoid of emotion. ' )
elif cmd . startswith ( ' f ' ) :
# Flag
self . _tellFlag ( forum )
elif cmd . startswith ( ' h ' ) :
# Help
2010-01-19 15:11:59 -07:00
forum . msg ( ''' Goal: Help me with my math homework, FROM ANOTHER DIMENSION! Order of operations is always left to right in that dimension, but the operators are alien. ''' )
2009-10-07 10:50:18 -06:00
forum . msg ( ' Order of operations is always left to right. ' )
2009-10-05 15:30:37 -06:00
#forum.msg('Goal: The current winner gets to control the contest music.')
forum . msg ( ' Commands: !help, !flag, !register [TEAM], !solve SOLUTION,!? EQUATION, !ops, !problem, !who ' )
2009-10-05 14:33:30 -06:00
elif cmd . startswith ( ' prob ' ) :
self . _tellPuzzle ( forum )
elif cmd . startswith ( ' solve ' ) and args :
# Solve
team = self . _affiliations . get ( who )
lastAttempt = time . time ( ) - self . _lastAttempt . get ( team , 0 )
2010-01-19 15:11:59 -07:00
#UN-COMMENT AFTER NMT CTF
# self._lastAttempt[team] = time.time()
2009-10-05 14:33:30 -06:00
answer = badmath . solve ( self . _key , self . _puzzle )
try :
attempt = int ( ' ' . join ( args ) . strip ( ) )
except :
forum . msg ( " %s : Answers are always integers. " )
if not team :
forum . msg ( ' %s : register first (!register TEAM). ' % who )
elif self . _flag . flag == team :
forum . msg ( ' %s : Greedy, greedy. ' % who )
elif lastAttempt < self . MAX_ATTEMPT_RATE :
forum . msg ( ' %s : Wait at least %d seconds between attempts ' %
( team , self . MAX_ATTEMPT_RATE ) )
elif answer == attempt :
self . _flag . set_flag ( team )
self . _lvl = self . _lvl + 1
self . _tellFlag ( forum )
self . _newPuzzle ( )
self . _tellPuzzle ( forum )
# self._giveToken(who, sender)
self . _saveState ( )
else :
forum . msg ( ' %s : That is not correct. ' % who )
# Test a simple one op command.
elif cmd . startswith ( ' ? ' ) :
2009-10-07 10:50:18 -06:00
if not args :
forum . msg ( ' %s : Give me an easier problem, and I \' ll '
' give you the answer. ' % who )
return
2009-10-05 14:33:30 -06:00
try :
tokens = badmath . parse ( ' ' . join ( args ) )
except ( ValueError ) as msg :
forum . msg ( ' %s : %s ' % ( who , msg ) )
return
if len ( tokens ) > 3 :
forum . msg ( ' %s : You can only test one op at a time. ' % who )
2010-01-19 15:11:59 -07:00
return
2009-10-05 14:33:30 -06:00
for num in self . _banned :
if num in tokens :
forum . msg ( ' %s : You can \' t test numbers in the '
' puzzle. ' % who )
return
try :
result = badmath . solve ( self . _key , tokens )
forum . msg ( ' %s : %s -> %d ' % ( who , ' ' . join ( args ) , result ) )
2010-01-19 15:11:59 -07:00
except Exception as msg :
forum . msg ( " %s : That doesn ' t work at all: %s " % ( who , msg ) )
2009-10-05 14:33:30 -06:00
elif cmd == ' birdzerk ' :
self . _saveState ( )
elif cmd == ' traceback ' :
forum . msg ( self . last_tb or ' No traceback ' )
if __name__ == ' __main__ ' :
import optparse
p = optparse . OptionParser ( )
2009-10-05 15:30:37 -06:00
p . add_option ( ' -i ' , ' --irc ' , dest = ' ircHost ' , default = ' localhost ' ,
help = ' IRC Host to connect to. ' )
2009-10-05 14:33:30 -06:00
p . add_option ( ' -f ' , ' --flagd ' , dest = ' flagd ' , default = ' localhost ' ,
2009-10-05 15:30:37 -06:00
help = ' Flag Server to connect to ' )
2009-10-05 14:33:30 -06:00
p . add_option ( ' -p ' , ' --password ' , dest = ' password ' ,
default = ' badmath:::a41c6753210c0bdafd84b3b62d7d1666 ' ,
help = ' Flag server password ' )
p . add_option ( ' -d ' , ' --path ' , dest = ' path ' , default = ' /var/lib/badmath ' ,
2009-10-05 15:30:37 -06:00
help = ' Path to where we can store state info. ' )
2009-10-07 10:50:18 -06:00
p . add_option ( ' -c ' , ' --channel ' , dest = ' channel ' , default = ' #badmath ' ,
2009-10-05 15:30:37 -06:00
help = ' Which channel to join ' )
2009-10-05 14:33:30 -06:00
opts , args = p . parse_args ( )
2009-10-05 15:30:37 -06:00
channels = [ opts . channel ]
2009-10-05 14:33:30 -06:00
2009-10-05 15:30:37 -06:00
flagger = Flagger ( opts . flagd , opts . password . encode ( ' utf-8 ' ) )
gyopi = Gyopi ( ( opts . ircHost , 6667 ) , channels , opts . path , flagger )
2009-10-05 14:33:30 -06:00
irc . run_forever ( )