|
| 1 | +import asyncore, random |
| 2 | +from hashlib import sha512 |
| 3 | +from datetime import datetime, timedelta |
| 4 | +from ConfigParser import ConfigParser |
| 5 | +from sys import argv, exit |
| 6 | +from ircasync import * |
| 7 | +from subprocess import Popen, PIPE |
| 8 | + |
| 9 | +ETHICAL_MESSAGES = [ |
| 10 | + [ # unethical |
| 11 | + 'your chairman resigns after the plan is revealed to the public by "60 Minutes".', |
| 12 | + 'your company was fined 1.4m$ after a probe into your activities.', |
| 13 | + 'your company was fined a record 600m$ by the EU after reports of bribery.', |
| 14 | + 'accounting irregularities were exposed by a whistleblower, resulting in a fine for your company.', |
| 15 | + 'the prime minister announces new taxes on employees because of a percieved lack of local investment by your company in rural areas.', |
| 16 | + ], |
| 17 | + |
| 18 | + [ # ethical |
| 19 | + 'a jury found no misconduct by board members.', |
| 20 | + 'testimony of key witnesses had to be striken from the record after finding they were all under the influence of drugs.', |
| 21 | + 'the prime minister issued a public apology over the government\'s handling of your company\'s case in the media.', |
| 22 | + |
| 23 | + ], |
| 24 | + |
| 25 | + [ # unsure |
| 26 | + 'a royal commission is establisted into your activities.', |
| 27 | + 'the government announces a bailout package for your company.', |
| 28 | + 'your company\'s charity activities in the third world were recognised by the judge in giving your company a lenient sentence.', |
| 29 | + 'your company owns a 70% share in local newspapers and television stations, meaning this incident is never brought to the public\'s attention.', |
| 30 | + ], |
| 31 | +] |
| 32 | + |
| 33 | +ETHICAL_STATES = [ |
| 34 | + 'unethical', |
| 35 | + 'ethical', |
| 36 | + 'unsure', |
| 37 | +] |
| 38 | + |
| 39 | +config = ConfigParser() |
| 40 | +try: |
| 41 | + config.readfp(open(argv[1])) |
| 42 | +except: |
| 43 | + try: |
| 44 | + config.readfp(open('ethicsbot.ini')) |
| 45 | + except: |
| 46 | + print "Syntax:" |
| 47 | + print " %s [config]" % argv[0] |
| 48 | + print "" |
| 49 | + print "If no configuration file is specified or there was an error, it will default to `ethicsbot.ini'." |
| 50 | + print "If there was a failure reading the configuration, it will display this message." |
| 51 | + exit(1) |
| 52 | + |
| 53 | +# read config |
| 54 | +SERVER = config.get('ethicsbot', 'server') |
| 55 | +try: PORT = config.getint('ethicsbot', 'port') |
| 56 | +except: PORT = DEFAULT_PORT |
| 57 | +NICK = config.get('ethicsbot', 'nick') |
| 58 | +CHANNEL = config.get('ethicsbot', 'channel') |
| 59 | +VERSION = 'ethicsbot hg:%s; http://hg.micolous.id.au/ircbots/' |
| 60 | +try: VERSION = VERSION % Popen(["hg","id"], stdout=PIPE).communicate()[0].strip() |
| 61 | +except: VERSION = VERSION % 'unknown' |
| 62 | +del Popen, PIPE |
| 63 | + |
| 64 | +try: FLOOD_COOLDOWN = timedelta(seconds=config.getint('ethicsbot', 'flood_cooldown')) |
| 65 | +except: FLOOD_COOLDOWN = timedelta(seconds=5) |
| 66 | +try: NICKSERV_PASS = config.get('ethicsbot', 'nickserv_pass') |
| 67 | +except: NICKSERV_PASS = None |
| 68 | + |
| 69 | +message_buffer = [] |
| 70 | +last_message = datetime.now() |
| 71 | +flooders = [] |
| 72 | +ignore_list = [] |
| 73 | + |
| 74 | +if config.has_section('ignore'): |
| 75 | + for k,v in config.items('ignore'): |
| 76 | + try: |
| 77 | + ignore_list.append(re.compile(v, re.I)) |
| 78 | + except Exception, ex: |
| 79 | + print "Error compiling regular expression in ignore list (%s):" % k |
| 80 | + print " %s" % v |
| 81 | + print ex |
| 82 | + exit(1) |
| 83 | + |
| 84 | +# main code |
| 85 | + |
| 86 | + |
| 87 | +def handle_msg(event, match): |
| 88 | + global message_buffer, MAX_MESSAGES, last_message, flooders, CHANNEL |
| 89 | + msg = event.text |
| 90 | + |
| 91 | + if event.channel.lower() != CHANNEL.lower(): |
| 92 | + # ignore messages not from our channel |
| 93 | + return |
| 94 | + |
| 95 | + if msg.startswith('?ethical'): |
| 96 | + for item in ignore_list: |
| 97 | + if item.search(event.origin) != None: |
| 98 | + # ignore list item hit |
| 99 | + print "Ignoring message from %s because of: %s" % (event.origin, item.pattern) |
| 100 | + return |
| 101 | + |
| 102 | + # now flood protect! |
| 103 | + delta = event.when - last_message |
| 104 | + last_message = event.when |
| 105 | + |
| 106 | + if delta < FLOOD_COOLDOWN: |
| 107 | + # 5 seconds between requests |
| 108 | + # any more are ignored |
| 109 | + print "Flood protection hit, %s of %s seconds were waited" % (delta.seconds, FLOOD_COOLDOWN.seconds) |
| 110 | + return |
| 111 | + |
| 112 | + parts = msg.split(' ') |
| 113 | + query = (''.join(parts[1:])).lower() |
| 114 | + |
| 115 | + if len(query) == 0: |
| 116 | + event.reply("%s: you must give me an ethical conundrum to process!" % event.nick) |
| 117 | + return |
| 118 | + |
| 119 | + # hash the request |
| 120 | + h = sha512() |
| 121 | + h.update(query) |
| 122 | + |
| 123 | + ethical = 0 |
| 124 | + for c in h.digest(): |
| 125 | + for x in xrange(1,9): |
| 126 | + if ord(c) & (2 ** x) > 1: |
| 127 | + ethical = (ethical + 1) % 3 |
| 128 | + |
| 129 | + event.reply('%s: (%s) %s' % (event.nick, ETHICAL_STATES[ethical], random.choice(ETHICAL_MESSAGES[ethical]))) |
| 130 | + |
| 131 | +def handle_welcome(event, match): |
| 132 | + global NICKSERV_PASS |
| 133 | + # Compliance with most network's rules to set this mode on connect. |
| 134 | + event.connection.usermode("+B") |
| 135 | + if NICKSERV_PASS != None: |
| 136 | + event.connection.todo(['NickServ', 'identify', NICKSERV_PASS]) |
| 137 | + |
| 138 | +irc = IRC(nick=NICK, start_channels=[CHANNEL], version=VERSION) |
| 139 | +irc.bind(handle_msg, PRIVMSG) |
| 140 | +irc.bind(handle_welcome, RPL_WELCOME) |
| 141 | + |
| 142 | +irc.make_conn(SERVER, PORT) |
| 143 | +asyncore.loop() |
| 144 | + |
0 commit comments