source: trollforge/troll/dramabot.py @ 489

Revision 307, 8.7 KB checked in by incog, 3 years ago (diff)
Line 
1# dramabot
2# a text generation IRC technology
3# by rolloffle, 2006
4
5from httplib import HTTPConnection
6import Queue
7from random import choice, randrange
8from re import compile
9from socks import socks4a  # if using Tor on 127.0.0.1:9050
10import threading
11from time import sleep
12from urllib import urlencode
13from socket import AF_INET, SOCK_STREAM, socket
14
15DENOUEMENTS = ('oops', 'oh hi', 'oh... hi', 'eurgh, shit', 'oh shit', 'oh fuck', 'speak of the devil', 'good timing', 'well this is akward, heh', 'this is awkward...')
16INSULTS = (
17        '%s is such a fat faggot',
18        '%s is such a faggot',
19        '%s\'s a fucking tool',
20        '%s is a judenspy',
21        'good thing %s isn\'t here to see that',
22        'im gonna tell %s you said that',
23        'tell %s to fuck off already',
24        '%s is a complete attention whore',
25        '%s is a worthless jew, why do we even keep him around?',
26        'who the fuck invited %s here',
27        '%s is a useless goblinkike',
28        'nobody wants %s around but don\'t mention it',
29        '%s is a complete cocksucking aspie',
30        'i know exactly what you mean about %s',
31        'did you hear the rumours about %s?',
32        '%s is a loser too',
33        'but I won\'t tell %s you said that',
34        '%s is a fucking spastic',
35        '%s is a cunt',
36)
37OPENINGS = (
38        'yeah, %s,',
39        'yeah %s',
40        'totally %s,',
41        'srsly %s,',
42        'agree 100%%, %s,',
43        'agreed %s,',
44        '%s backs me up on this,',
45        'yah, %s told me,',
46        'i agree %s,'
47)
48
49def rand_char():
50        return chr(randrange(ord('a'), ord('z')))
51
52def gen_rand_username():
53        #return 'wqat'
54        return ''.join([rand_char() for i in range(8)])
55
56class channel:
57
58        def __init__(self, con, chan):
59                """Initialise, join the channel."""
60
61                self.con = con
62                self.chan = chan
63                self.nicks = [ ]
64                self.con.lsend('JOIN ' + chan)
65
66        def rand_nick(self):
67                """Returns a random nick present in this channel."""
68                return choice(self.nicks)
69
70        def strip_nick_status_symbol(self, nick):
71                """If present, removes the status character from a nick."""
72                if nick[0] in '@%+':
73                        nick = nick[1:]
74                return nick
75
76        def add_nicks(self, nicks):
77                """Add a list of nicks to this channel's nick list."""
78                for nick in nicks:
79                        nick = self.strip_nick_status_symbol(nick)
80                        if (nick != self.con.nick) and (nick not in self.nicks):
81                                self.nicks += [ nick ]
82
83        def del_nick(self, nick):
84                """Delete a nick from this channel's nick list."""
85                nick = self.strip_nick_status_symbol(nick)
86                try:
87                        self.nicks.remove(nick)
88                except:
89                        print 'Error: failed to remove \'%s\' from nick list for %s' % (nick, self.chan)
90
91        def drama_bomb(self, target):
92                """Say something in the channel to a target nick to provoke them."""
93                global DENOUEMENTS, INSULTS, OPENINGS
94
95                template = 'PRIVMSG %s :' + choice(OPENINGS) + ' ' + choice(INSULTS)
96                print "TEMPLATE: " + template
97                bomb = template % (self.chan, self.rand_nick(), target)
98
99                print '%s: drama bomb "%s"' % (self.chan, bomb)
100                self.con.lsend(bomb)
101
102                if randrange(1, 2) != 1:
103                        # chance of saying a denouement
104                        sleep(1)
105                        self.con.lsend('PRIVMSG %s :%s' % (self.chan, choice(DENOUEMENTS)))
106
107        def display_status(self):
108                """Write a status report for this channel on stdout."""
109                print %s: new nick list length %u' % (self.chan, len(self.nicks))
110
111class con:
112
113        def __init__(self, server, port, channels, nick, socks4a_proxy = None):
114
115                self.server, self.port, self.nick = server, port, nick
116
117                self.r = { }
118                self.r['001'] = compile(':[^ ]+ 001 ')
119                self.r['353'] = compile(':[^ ]+ 353 [^ ]+ . ([^ ]+) :(.+)')
120                self.r['433'] = compile(':[^ ]+ 433 . [^ ]+ :.+')
121                self.r['join'] = compile(':([^!~:]+)![^ ]+ JOIN :([^ ]+)')
122                self.r['part'] = compile(':([^!~:]+)![^ ]+ PART ([^ ]+)( :(.+))?')
123                self.r['kick'] = compile(':([^!~:]+)![^ ]+ KICK ([^ ]+) ([^ ]+)( :(.+))?')
124                self.r['privmsg'] = compile(':([^!~:]+)![^ ]+ PRIVMSG ([^ ]+) :(.+)')
125                self.r['http'] = compile(':([^!~:]+)![^ ]+ PRIVMSG ([^ ]+) :.*http://.*')
126                self.r['die'] = compile(':([^!~:]+)![^ ]+ PRIVMSG ([^ ]+) :' + nick + ': die')
127
128                self.connect(socks4a_proxy)
129
130                self.channels = { }
131                for chan in channels:
132                        self.channels[chan] = channel(self, chan)
133
134        def lsend(self, s):
135                self.sock.send(s + '\r\n')
136
137        def lrecv(self):
138                c, s = '', ''
139                while c != '\n':
140                        c = self.sock.recv(1)
141                        if c == '':  # connection closed
142                                break
143                        s += c
144                return s.strip('\r\n')
145
146        def connect(self, socks4a_proxy = None):
147                """Make the initial connection to the server and try to register."""
148
149                self.sock = socks4a()
150
151                print 'Connecting to %s:%u' % (self.server, self.port)
152                if socks4a_proxy == None:
153                        self.sock.connect((self.server, self.port))
154                else:
155                        self.sock.proxy_connect(socks4a_proxy, (self.server, self.port))
156
157                username = gen_rand_username()
158                print '  username = ' + username
159                self.lsend('USER ' + username + ' 0 0 :Unknown')
160                self.lsend('NICK ' + self.nick + ' 0')
161
162                # Wait for the 001 status reply.
163                while 1:
164                        line = self.lrecv()
165                        if self.r['001'].match(line):
166                                # We got the 001, break out of the loop and proceed.
167                                break
168                        elif self.r['433'].match(line):
169                                # This nick is taken. Change the last 2 chars and retry.
170                                self.nick = self.nick[:-2] + rand_char() + rand_char()
171                                self.lsend('NICK ' + self.nick + ' 0')
172                        elif line == '':
173                                raise 'ConnectError', (self.server, self.port, 'EOFBefore001')
174
175                print '  got 001'
176
177        def go(self):
178
179                global rand_char
180
181                # Join the channels.
182                print '  joining channels'
183                for channel in self.channels:
184                        self.lsend('JOIN ' + channel)
185                print '  joined all channels'
186
187                # Sit and watch at the socket for incoming data and act on it.
188
189                while 1:
190
191                        line = self.lrecv()
192
193                        if line == '':
194                                raise 'ConnectionClose', (self.server, self.port)
195
196                        elif line[:6] == 'PING :':
197                                print '  PONG :' + line[6:]
198                                self.lsend('PONG :' + line[6:])
199                                continue
200
201                        m = self.r['join'].match(line)
202                        if m != None:
203                                # Somebody joined. If it's not us, start the drama and add
204                                # their nick to the master list of nicks in this channel.
205                                print 'Received: JOIN'
206
207                                if m.group(1) == self.nick:
208                                        continue
209
210                                chan = self.channels[m.group(2)]
211
212                                chan.add_nicks([ m.group(1) ])
213                                chan.display_status()
214                                chan.drama_bomb(m.group(1))
215                               
216                                continue
217
218                        m = self.r['part'].match(line)
219                        if m != None:
220                                # Somebody parted, remove their nick from the master list of
221                                # this channel's nicks.
222                                print 'Received: PART'
223                                chan = self.channels[m.group(2)]
224                                chan.del_nick(m.group(1))
225                                chan.display_status()
226
227                        m = self.r['353'].match(line)
228                        if m != None:
229                                # 353 lists the nicks of the user in a channel we just
230                                # joined. Add the list of nicks given to the master list of
231                                # nicks present in the channel.
232                                print 'Received: 353'
233                                chan = self.channels[m.group(1)]
234                                chan.add_nicks(m.group(2).split())
235                                chan.display_status()
236                                continue
237
238                        m = self.r['kick'].match(line)
239                        if m != None:
240                                # Someone was kicked.
241                                if m.group(3) == self.nick:
242                                        # It was us! Rejoin.
243                                        print 'Kicked! Attempting to rejoin ' + m.group(2)
244                                        self.lsend('JOIN ' + m.group(2))
245                        line = self.lrecv()
246
247                        if line == '':
248                                raise 'ConnectionClose', (self.server, self.port)
249
250                        elif line[:6] == 'PING :':
251                                print '  PONG :' + line[6:]
252                                self.lsend('PONG :' + line[6:])
253                                continue
254
255                        print line
256
257                        m = self.r['die'].match(line)
258                        if m != None:
259                                print 'Request: die'
260                                nick = m.group(1).lower().replace('m', '/\\/\\')
261                                out_queue.put( [m.group(2), 'f u' + nick] )
262                                print 'response sent'
263
264                        m = self.r['http'].match(line)
265                        if m != None:
266                                # Someone said a URL.
267                                reply = [
268                                        '/!\\ FUCKIN\' OLD /!\\',
269                                        '404',
270                                        'broken link',
271                                        'busted link',
272                                        'link fails',
273                                        'LM link dont click!',
274                                        'LastMeasure link detected',
275                                        'OLD',
276                                        'CP link',
277                                        'LM link',
278                                        'CP',
279                                        'LM',
280                                        'OLD i posted that yesterday'
281                                ]
282                                out_queue.put( [ m.group(2), choice(reply) ] )
283                                continue
284
285class con_thread(threading.Thread):
286
287        def __init__(self, server, port, channels, nick):
288                self.co = con(server, port, channels, nick)
289                threading.Thread.__init__(self)
290
291        def run(self):
292                self.co.go()
293                #self.co.go(('127.0.0.1', 9050))  # connect via Tor
294
295# Create the queue to store events to be announced.
296out_queue = Queue.Queue(999)
297
298threads = [
299        con_thread('irc.ww88.org', 6667, ['#gnaa'], 'dramabot'),
300        #con_thread('irc.chir.pn', 6667, ['#chirp'], 'dramabot')
301
302        # The wacky .onion hostname is because it's Freenode's hidden Tor service
303        #con_thread('mejokbp2brhw4omd.onion', 6667, ['#wikipedia'], 'fade')
304]
305
306for thread in threads:
307        thread.setDaemon(1)
308        thread.start()
309
310while 1:
311        try:
312                event = out_queue.get(True)
313        except KeyboardInterrupt:
314                break
315        for thread in threads:
316                thread.co.lsend('PRIVMSG ' + event[0] + ' :' + event[1])
Note: See TracBrowser for help on using the repository browser.