~ubuntu-branches/ubuntu/utopic/gozerbot/utopic

« back to all changes in this revision

Viewing changes to build/lib/gplugs/udp.py

  • Committer: Package Import Robot
  • Author(s): Jeremy Malcolm
  • Date: 2012-04-03 21:58:28 UTC
  • mfrom: (3.1.11 sid)
  • Revision ID: package-import@ubuntu.com-20120403215828-6mik0tzug5na93la
Tags: 0.99.1-2
* Removes logfiles on purge (Closes: #668767)
* Reverted location of installed files back to /usr/lib/gozerbot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# plugs/udp.py
 
2
#
 
3
#
 
4
 
 
5
"""
 
6
    the bot has the capability to listen for udp packets which it will use
 
7
    to /msg a given nick or channel.
 
8
 
 
9
    1) setup
 
10
 
 
11
        * do !reload udp to enable the udp plugin
 
12
        * call !udp-cfgsave to generate a config file in gozerdata/plugs/udp/config
 
13
        * edit this file .. esp. look at the udpallowednicks list
 
14
        * run ./bin/gozerbot-udp -s to generate clientside config file "udp-send"
 
15
        * edit this file
 
16
        * test with:
 
17
 
 
18
        ::
 
19
 
 
20
            echo "YOOO" | ./bin/gozerbot-udp
 
21
 
 
22
    2) limiter
 
23
 
 
24
        on IRC the bot's /msg to a user/channel are limited to 1 per 3 seconds so the
 
25
        bot will not excessflood on the server. you can use partyudp if you need no 
 
26
        delay between sent messages, this will use dcc chat to deliver the message.
 
27
        on jabber bots there is no delay
 
28
 
 
29
"""
 
30
 
 
31
 
 
32
__copyright__ = 'this file is in the public domain'
 
33
 
 
34
# IMPORT SECTION
 
35
 
 
36
from gozerbot.fleet import fleet
 
37
from gozerbot.generic import rlog, handle_exception, strippedtxt, lockdec
 
38
from gozerbot.config import config
 
39
from gozerbot.plughelp import plughelp
 
40
from gozerbot.partyline import partyline
 
41
from gozerbot.threads.thr import start_new_thread
 
42
from gozerbot.contrib.rijndael import rijndael
 
43
from gozerbot.persist.persistconfig import PersistConfig
 
44
 
 
45
import socket, re, time, Queue
 
46
 
 
47
# END IMPORT
 
48
 
 
49
plughelp.add('udp' , 'run the udp listen thread')
 
50
 
 
51
# VARS SECTION
 
52
 
 
53
cfg = PersistConfig()
 
54
cfg.define('udp', 1) # set to 0 to disnable
 
55
cfg.define('udpparty', 0)
 
56
cfg.define('udpipv6', 0)
 
57
cfg.define('udpmasks', ['192.168*', ])
 
58
cfg.define('udphost', "localhost")
 
59
cfg.define('udpport', 5500)
 
60
cfg.define('udpallow', ["127.0.0.1", ])
 
61
cfg.define('udpallowednicks', ["#gozerbot", "dunker"])
 
62
cfg.define('udppassword', "mekker", exposed=False)
 
63
cfg.define('udpseed', "blablablablablaz", exposed=False) # needs to be 16 chars wide
 
64
cfg.define('udpstrip', 1) # strip all chars < char(32)
 
65
cfg.define('udpsleep', 0) # sleep in sendloop .. can be used to delay pack
 
66
cfg.define('udpdblog', 0)
 
67
cfg.define('udpbots', [cfg['udpbot'] or 'default', ])
 
68
 
 
69
# END VARS
 
70
 
 
71
def _inmask(addr):
 
72
 
 
73
    """ check if addr matches a mask. """
 
74
 
 
75
    if not cfg['udpmasks']:
 
76
        return False
 
77
    for i in cfg['udpmasks']:
 
78
        i = i.replace('*', '.*')
 
79
        if re.match(i, addr):
 
80
            return True
 
81
 
 
82
class Udplistener(object):
 
83
 
 
84
    """ 
 
85
        listen for udp messages.
 
86
 
 
87
    """
 
88
 
 
89
    def __init__(self):
 
90
        self.outqueue = Queue.Queue()
 
91
        self.queue = Queue.Queue()
 
92
        self.stop = 0
 
93
        if cfg['udpipv6']:
 
94
            self.sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
 
95
        else:
 
96
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
97
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
98
        try:
 
99
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
 
100
        except:
 
101
            pass
 
102
        self.sock.setblocking(1)
 
103
        self.sock.settimeout(1)
 
104
        self.loggers = []
 
105
 
 
106
    def _outloop(self):
 
107
 
 
108
        """ 
 
109
            loop controling the rate of outputted messages.
 
110
 
 
111
            .. literalinclude:: ../../gozerplugs/udp.py
 
112
                :pyobject: Udplistener._outloop
 
113
 
 
114
        """
 
115
 
 
116
        rlog(5, 'udp', 'starting outloop')
 
117
        while not self.stop:
 
118
            (printto, txt) = self.outqueue.get()
 
119
            if self.stop:
 
120
                return
 
121
            self.dosay(printto, txt)
 
122
        rlog(5, 'udp', 'stopping outloop')
 
123
 
 
124
    def _handleloop(self):
 
125
        while not self.stop:
 
126
            (input, addr) = self.queue.get()
 
127
            if not input or not addr:
 
128
                continue
 
129
            if self.stop:
 
130
                break                
 
131
            self.handle(input, addr)
 
132
            if cfg['udpsleep']:
 
133
                time.sleep(cfg['udpsleep'] or 0.01)
 
134
        rlog(5, 'udp', 'shutting down udplistener')
 
135
 
 
136
    def _listen(self):
 
137
        """ listen for udp messages .. /msg via bot"""
 
138
        if not cfg['udp']:
 
139
            return
 
140
        for botname in cfg['udpbots']:
 
141
            if not fleet.byname(botname):
 
142
                rlog(10, 'udp', "can't find %s bot" % botname)
 
143
                
 
144
        try:
 
145
            fleet.startok.wait(5)
 
146
            self.sock.bind((cfg['udphost'], cfg['udpport']))
 
147
            rlog(10, 'udp', 'udp listening on %s %s' % (cfg['udphost'], \
 
148
cfg['udpport']))
 
149
            self.stop = 0
 
150
        except IOError:
 
151
            handle_exception()
 
152
            self.sock = None
 
153
            self.stop = 1
 
154
            return
 
155
        # loop on listening udp socket
 
156
        while not self.stop:
 
157
            try:
 
158
                input, addr = self.sock.recvfrom(64000)
 
159
            except socket.timeout:
 
160
                continue
 
161
            except Exception, ex:
 
162
                try:
 
163
                    (errno, errstr) = ex
 
164
                except ValueError:
 
165
                    errno = 0
 
166
                    errstr = str(ex)
 
167
                if errno == 4:
 
168
                    rlog(10, self.name, str(ex))
 
169
                    break
 
170
                if errno == 35:
 
171
                    continue
 
172
                else:
 
173
                    handle_exception()
 
174
                    break
 
175
            if self.stop:
 
176
                break
 
177
            self.queue.put((input, addr))
 
178
        rlog(5, 'udp', 'shutting down main loop')
 
179
 
 
180
    def handle(self, input, addr):
 
181
 
 
182
        """ 
 
183
            handle an incoming udp packet. 
 
184
 
 
185
            :param input: txt in udp packet
 
186
            :type input: string
 
187
            :param addr: address info of udp packet
 
188
            :type add: (host, port) tuple
 
189
 
 
190
            .. literalinclude:: ../../gozerplugs/udp.py
 
191
                :pyobject: Udplistener.handle
 
192
        """
 
193
 
 
194
        if cfg['udpseed']:
 
195
            data = ""
 
196
            for i in range(len(input)/16):
 
197
                try:
 
198
                    data += crypt.decrypt(input[i*16:i*16+16])
 
199
                except Exception, ex:
 
200
                    rlog(10, 'udp', "can't decrypt: %s" % str(ex))
 
201
                    data = input
 
202
                    break
 
203
        else:
 
204
            data = input
 
205
        if cfg['udpstrip']:
 
206
            data = strippedtxt(data)
 
207
        # check if udp is enabled and source ip is in udpallow list
 
208
        if cfg['udp'] and (addr[0] in cfg['udpallow'] or \
 
209
_inmask(addr[0])):
 
210
            # get printto and passwd data
 
211
            header = re.search('(\S+) (\S+) (.*)', data)
 
212
            if header:
 
213
                # check password
 
214
                if header.group(1) == cfg['udppassword']:
 
215
                    printto = header.group(2)    # is the nick/channel
 
216
                    # check if printto is in allowednicks
 
217
                    if not printto in cfg['udpallowednicks']:
 
218
                        rlog(10, 'udp', "udp denied %s" % printto )
 
219
                        return
 
220
                    rlog(0, 'udp', str(addr[0]) +  " udp allowed")
 
221
                    text = header.group(3)    # is the text
 
222
                    self.say(printto, text)
 
223
                else:
 
224
                    rlog(10, 'udp', "can't match udppasswd from " + \
 
225
str(addr))
 
226
            else:
 
227
                rlog(10, 'udp', "can't match udp from " + str(addr[0]))
 
228
        else:
 
229
            rlog(10, 'udp', 'denied udp from ' + str(addr[0]))
 
230
 
 
231
    def say(self, printto, txt):
 
232
 
 
233
        """ 
 
234
            send txt to printto. 
 
235
 
 
236
            .. literalinclude:: ../../gozerplugs/udp.py
 
237
                :pyobject: Udplistener.say
 
238
 
 
239
        """
 
240
 
 
241
        self.outqueue.put((printto, txt))
 
242
 
 
243
    def dosay(self, printto, txt):
 
244
 
 
245
        """ 
 
246
            send txt to printto .. do some checks. 
 
247
 
 
248
           .. literalinclude:: ../../gozerplugs/udp.py
 
249
                :pyobject: Udplistener.dosay
 
250
 
 
251
        """
 
252
 
 
253
        if cfg['udpparty'] and partyline.is_on(printto):
 
254
            partyline.say_nick(printto, txt)
 
255
            return
 
256
        if not cfg['udpbots']:
 
257
            bots = [cfg['udpbot'], ]
 
258
        else:
 
259
            bots = cfg['udpbots']
 
260
        for botname in bots:
 
261
            bot = fleet.byname(botname)
 
262
            if not bot:
 
263
                rlog(10, 'udp', "can't find %s bot in fleet" % botname)
 
264
                continue
 
265
            #if not bot.jabber and not cfg['nolimiter']:
 
266
            #    time.sleep(3)
 
267
            bot.connectok.wait()
 
268
            bot.say(printto, txt)
 
269
            for i in self.loggers:
 
270
                i.log(printto, txt)
 
271
 
 
272
# the udplistener object
 
273
if cfg['udp']:
 
274
    udplistener = Udplistener()
 
275
 
 
276
# initialize crypt object if udpseed is set in config
 
277
if cfg['udp'] and cfg['udpseed']:
 
278
    crypt = rijndael(cfg['udpseed'])
 
279
 
 
280
def init():
 
281
 
 
282
    """ 
 
283
        init the udp plugin. 
 
284
 
 
285
        .. literalinclude:: ../../gozerplugs/udp.py
 
286
            :pyobject: init
 
287
 
 
288
    """
 
289
 
 
290
    if cfg['udp']:
 
291
        start_new_thread(udplistener._listen, ())
 
292
        start_new_thread(udplistener._handleloop, ())
 
293
        start_new_thread(udplistener._outloop, ())
 
294
    return 1
 
295
    
 
296
def shutdown():
 
297
 
 
298
    """ 
 
299
        shutdown the udp plugin.
 
300
 
 
301
        .. literalinclude:: ../../gozerplugs/udp.py
 
302
            :pyobject: init
 
303
 
 
304
    """
 
305
 
 
306
    if cfg['udp'] and udplistener:
 
307
        udplistener.stop = 1
 
308
        udplistener.outqueue.put_nowait((None, None))
 
309
        udplistener.queue.put_nowait((None, None))
 
310
    return 1
 
311
 
 
312
if cfg['udp'] and cfg['udpdblog']:
 
313
 
 
314
    from gozerbot.database.db import Db
 
315
 
 
316
    class Udpdblog:
 
317
 
 
318
        """ log udp data to database. """
 
319
 
 
320
        # see tables/udplog for table definition and add udpdblog = 1 to 
 
321
        # the config file
 
322
        db = Db()
 
323
 
 
324
        def log(self, printto, txt):
 
325
 
 
326
            """ do the actual logging. """
 
327
 
 
328
            try:
 
329
                res = self.db.execute("""INSERT into udplog(time,printto,txt)
 
330
values(%s,%s,%s) """, (time.time(), printto, txt))
 
331
            except Exception, ex:
 
332
                rlog(10, 'udp', 'failed to log to db: %s' % str(ex))
 
333
            return res
 
334
 
 
335
    udplistener.loggers.append(Udpdblog())
 
336
    rlog(10, 'udp', 'registered database udp logger')