~ubuntu-ru-irc/+junk/irckit

« back to all changes in this revision

Viewing changes to ubuntuhelp/ubuntubot/plugins/ChannelLogger/plugin.py

  • Committer: rmPIC30 at gmail
  • Date: 2010-07-13 09:08:58 UTC
  • Revision ID: rmpic30@gmail.com-20100713090858-w5kkmk093hx38cen
Добавляю бота

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
###
 
2
# Copyright (c) 2002-2004, Jeremiah Fincher
 
3
# All rights reserved.
 
4
#
 
5
# Redistribution and use in source and binary forms, with or without
 
6
# modification, are permitted provided that the following conditions are met:
 
7
#
 
8
#   * Redistributions of source code must retain the above copyright notice,
 
9
#     this list of conditions, and the following disclaimer.
 
10
#   * Redistributions in binary form must reproduce the above copyright notice,
 
11
#     this list of conditions, and the following disclaimer in the
 
12
#     documentation and/or other materials provided with the distribution.
 
13
#   * Neither the name of the author of this software nor the name of
 
14
#     contributors to this software may be used to endorse or promote products
 
15
#     derived from this software without specific prior written consent.
 
16
#
 
17
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
18
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
19
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
20
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
21
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
22
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
23
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
24
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
25
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
26
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
27
# POSSIBILITY OF SUCH DAMAGE.
 
28
###
 
29
 
 
30
import os
 
31
import time
 
32
from cStringIO import StringIO
 
33
 
 
34
import supybot.conf as conf
 
35
import supybot.world as world
 
36
import supybot.irclib as irclib
 
37
import supybot.ircmsgs as ircmsgs
 
38
import supybot.ircutils as ircutils
 
39
import supybot.registry as registry
 
40
import supybot.callbacks as callbacks
 
41
 
 
42
class FakeLog(object):
 
43
    def flush(self):
 
44
        return
 
45
    def close(self):
 
46
        return
 
47
    def write(self, s):
 
48
        return
 
49
 
 
50
class ChannelLogger(callbacks.Plugin):
 
51
    noIgnore = True
 
52
    def __init__(self, irc):
 
53
        self.__parent = super(ChannelLogger, self)
 
54
        self.__parent.__init__(irc)
 
55
        self.lastMsgs = {}
 
56
        self.lastStates = {}
 
57
        self.logs = {}
 
58
        self.flusher = self.flush
 
59
        world.flushers.append(self.flusher)
 
60
 
 
61
    def die(self):
 
62
        for log in self._logs():
 
63
            log.close()
 
64
        world.flushers = [x for x in world.flushers if x is not self.flusher]
 
65
 
 
66
    def __call__(self, irc, msg):
 
67
        try:
 
68
            # I don't know why I put this in, but it doesn't work, because it
 
69
            # doesn't call doNick or doQuit.
 
70
            # if msg.args and irc.isChannel(msg.args[0]):
 
71
            self.__parent.__call__(irc, msg)
 
72
            if irc in self.lastMsgs:
 
73
                if irc not in self.lastStates:
 
74
                    self.lastStates[irc] = irc.state.copy()
 
75
                self.lastStates[irc].addMsg(irc, self.lastMsgs[irc])
 
76
        finally:
 
77
            # We must make sure this always gets updated.
 
78
            self.lastMsgs[irc] = msg
 
79
 
 
80
    def reset(self):
 
81
        for log in self._logs():
 
82
            log.close()
 
83
        self.logs.clear()
 
84
        self.lastMsgs.clear()
 
85
        self.lastStates.clear()
 
86
 
 
87
    def _logs(self):
 
88
        for logs in self.logs.itervalues():
 
89
            for log in logs.itervalues():
 
90
                yield log
 
91
 
 
92
    def flush(self):
 
93
        self.checkLogNames()
 
94
        for log in self._logs():
 
95
            try:
 
96
                log.flush()
 
97
            except ValueError, e:
 
98
                if e.args[0] != 'I/O operation on a closed file':
 
99
                    self.log.exception('Odd exception:')
 
100
 
 
101
    def logNameTimestamp(self, channel):
 
102
        format = self.registryValue('filenameTimestamp', channel)
 
103
        return time.strftime(format)
 
104
 
 
105
    def getLogName(self, channel):
 
106
        if self.registryValue('rotateLogs', channel):
 
107
            return '%s.log' % (channel)
 
108
        #    return '%s.log' % (channel, self.logNameTimestamp(channel))
 
109
        else:
 
110
            return '%s.log' % channel
 
111
 
 
112
    def getLogDir(self, irc, channel):
 
113
        logDir = conf.supybot.directories.log.dirize(self.name())
 
114
        if self.registryValue('directories'):
 
115
            if self.registryValue('directories.network'):
 
116
                logDir = os.path.join(logDir,  irc.network)
 
117
            if self.registryValue('directories.channel'):
 
118
                logDir = os.path.join(logDir, channel)
 
119
            if self.registryValue('directories.timestamp'):
 
120
                format = self.registryValue('directories.timestamp.format')
 
121
                timeDir =time.strftime(format)
 
122
                logDir = os.path.join(logDir, timeDir)
 
123
        if not os.path.exists(logDir):
 
124
            os.makedirs(logDir)
 
125
        return logDir
 
126
 
 
127
    def checkLogNames(self):
 
128
        for (irc, logs) in self.logs.items():
 
129
            for (channel, log) in logs.items():
 
130
                if self.registryValue('rotateLogs', channel):
 
131
                    name = self.getLogName(channel)
 
132
                    if name != log.name:
 
133
                        log.close()
 
134
                        del logs[channel]
 
135
 
 
136
    def getLog(self, irc, channel):
 
137
        self.checkLogNames()
 
138
        try:
 
139
            logs = self.logs[irc]
 
140
        except KeyError:
 
141
            logs = ircutils.IrcDict()
 
142
            self.logs[irc] = logs
 
143
        if channel in logs:
 
144
            return logs[channel]
 
145
        else:
 
146
            try:
 
147
                name = self.getLogName(channel)
 
148
                logDir = self.getLogDir(irc, channel)
 
149
                log = file(os.path.join(logDir, name), 'a')
 
150
                logs[channel] = log
 
151
                return log
 
152
            except IOError:
 
153
                self.log.exception('Error opening log:')
 
154
                return FakeLog()
 
155
 
 
156
    def timestamp(self, log):
 
157
        format = conf.supybot.log.timestampFormat()
 
158
        if format:
 
159
            log.write(time.strftime(format))
 
160
            log.write('  ')
 
161
 
 
162
    def normalizeChannel(self, irc, channel):
 
163
        return ircutils.toLower(channel)
 
164
 
 
165
    def doLog(self, irc, channel, s, *args):
 
166
        if self.registryValue('enable', channel):
 
167
            s = format(s, *args)
 
168
            channel = self.normalizeChannel(irc, channel)
 
169
            log = self.getLog(irc, channel)
 
170
            if self.registryValue('timestamp', channel):
 
171
                self.timestamp(log)
 
172
            if self.registryValue('stripFormatting', channel):
 
173
                s = ircutils.stripFormatting(s)
 
174
            log.write(s)
 
175
            if self.registryValue('flushImmediately'):
 
176
                log.flush()
 
177
 
 
178
    def doPrivmsg(self, irc, msg):
 
179
        (recipients, text) = msg.args
 
180
        for channel in recipients.split(','):
 
181
            if irc.isChannel(channel):
 
182
                noLogPrefix = self.registryValue('noLogPrefix', channel)
 
183
                if noLogPrefix and text.startswith(noLogPrefix):
 
184
                    text = '-= THIS MESSAGE NOT LOGGED =-'
 
185
                nick = msg.nick or irc.nick
 
186
                if ircmsgs.isAction(msg):
 
187
                    self.doLog(irc, channel,
 
188
                             '* %s %s\n', nick, ircmsgs.unAction(msg))
 
189
                else:
 
190
                    self.doLog(irc, channel, '<%s> %s\n', nick, text)
 
191
 
 
192
    def doNotice(self, irc, msg):
 
193
        (recipients, text) = msg.args
 
194
        for channel in recipients.split(','):
 
195
            if irc.isChannel(channel):
 
196
                self.doLog(irc, channel, '-%s- %s\n', msg.nick, text)
 
197
 
 
198
    def doNick(self, irc, msg):
 
199
        oldNick = msg.nick
 
200
        newNick = msg.args[0]
 
201
        for (channel, c) in irc.state.channels.iteritems():
 
202
            if newNick in c.users:
 
203
                self.doLog(irc, channel,
 
204
                           '*** %s is now known as %s\n', oldNick, newNick)
 
205
    def doJoin(self, irc, msg):
 
206
        for channel in msg.args[0].split(','):
 
207
            self.doLog(irc, channel,
 
208
                       '*** %s has joined %s\n',
 
209
                       msg.nick or msg.prefix, channel)
 
210
 
 
211
    def doKick(self, irc, msg):
 
212
        if len(msg.args) == 3:
 
213
            (channel, target, kickmsg) = msg.args
 
214
        else:
 
215
            (channel, target) = msg.args
 
216
            kickmsg = ''
 
217
        if kickmsg:
 
218
            self.doLog(irc, channel,
 
219
                       '*** %s was kicked by %s (%s)\n',
 
220
                       target, msg.nick, kickmsg)
 
221
        else:
 
222
            self.doLog(irc, channel,
 
223
                       '*** %s was kicked by %s\n', target, msg.nick)
 
224
 
 
225
    def doPart(self, irc, msg):
 
226
        for channel in msg.args[0].split(','):
 
227
            self.doLog(irc, channel,
 
228
                       '*** %s has left %s\n', msg.nick, channel)
 
229
 
 
230
    def doMode(self, irc, msg):
 
231
        channel = msg.args[0]
 
232
        if irc.isChannel(channel) and msg.args[1:]:
 
233
            self.doLog(irc, channel,
 
234
                       '*** %s sets mode: %s %s\n',
 
235
                       msg.nick or msg.prefix, msg.args[1],
 
236
                        ' '.join(msg.args[2:]))
 
237
 
 
238
    def doTopic(self, irc, msg):
 
239
        if len(msg.args) == 1:
 
240
            return # It's an empty TOPIC just to get the current topic.
 
241
        channel = msg.args[0]
 
242
        self.doLog(irc, channel,
 
243
                   '*** %s changes topic to "%s"\n', msg.nick, msg.args[1])
 
244
 
 
245
    def doQuit(self, irc, msg):
 
246
        if not isinstance(irc, irclib.Irc):
 
247
            irc = irc.getRealIrc()
 
248
        for (channel, chan) in self.lastStates[irc].channels.iteritems():
 
249
            if msg.nick in chan.users:
 
250
                self.doLog(irc, channel, '*** %s has quit IRC\n', msg.nick)
 
251
 
 
252
    def outFilter(self, irc, msg):
 
253
        # Gotta catch my own messages *somehow* :)
 
254
        # Let's try this little trick...
 
255
        if msg.command in ('PRIVMSG', 'NOTICE'):
 
256
            # Other messages should be sent back to us.
 
257
            m = ircmsgs.IrcMsg(msg=msg, prefix=irc.prefix)
 
258
            self(irc, m)
 
259
        return msg
 
260
 
 
261
 
 
262
Class = ChannelLogger
 
263
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: