source: misc/swirc.py @ 6:ea25f7a285c6

Revision 6:ea25f7a285c6, 6.0 KB checked in by Eriol, 5 years ago (diff)

Clean up. The joining problem depends on IRC server!

Line 
1#!/usr/bin/env python
2#
3# swirc.py
4#
5# An IRC bot to analyze the Small World Phenonmenon in the IRC world.
6# Original idea: AlpT <alpt@freaknet.org>
7# For more details see: http://idiki.dyne.org/wiki/IRC_Small_World
8#
9# Copyright (C) 2007  Daniele Tricoli aka Eriol <eriol@mornie.org>
10#
11# This program is free software; you can redistribute it and/or
12# modify it under the terms of the GNU General Public License
13# as published by the Free Software Foundation; either version 2
14# of the License, or (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24#
25# --
26# Thanks to AlpT and KatolaZ for sharing this idea and coding with me at JD06!
27# Thanks to MancaUSoft for helping with the absurd problems' debug ;)
28#
29# python-irclib sucks, long life to twisted ;)
30
31import sys
32import pydot
33import time
34
35from twisted.words.protocols import irc
36from twisted.internet import reactor, protocol
37
38import logging as log
39log.basicConfig(level=log.DEBUG,
40                format='[%(asctime)s] %(levelname)s %(message)s',
41                datefmt='%d/%m/%Y %H:%M:%S %Z',
42                filename='swirc.log',
43                filemode='w')
44console = log.StreamHandler()
45console.setLevel(log.INFO)
46log.getLogger().addHandler(console)
47
48BOT_NICKNAME = 'swirc'
49MAX_CURRENT_CHANNEL = 10
50
51class Graph(object):
52
53    def __init__(self, sdata=None):
54        self.data = {}
55
56        if sdata is not None:
57            self.data.update(sdata)
58
59    def addNode(self, node, reachable):
60        try:
61            self.data[node]
62        except KeyError:
63            self.data[node] = reachable
64        else:
65            for e in reachable:
66                if e not in self.data[node]:
67                    self.data[node] += reachable
68
69    def __len__(self):
70        return len(self.data)
71
72class MakeGraph(object):
73
74    def __init__(self):
75        self.graph = Graph()
76
77    def addNick(self, nickslist):
78
79        for nick in nickslist:
80            self.graph.addNode(nick,
81                               [node for node in nickslist if node != nick])
82
83    def _cleanEdges(self):
84        edges = [(nick, x) for nick in self.graph.data
85                             for x in self.graph.data[nick]]
86
87        cleanedEdges = list(edges)
88
89        for x in edges:
90            if (x[1], x[0]) in cleanedEdges:
91                del cleanedEdges[cleanedEdges.index(x)]
92
93        return cleanedEdges
94
95    def saveDot(self, fname):
96        gr = pydot.graph_from_edges(self._cleanEdges())
97        gr.write_dot(fname, prog='dot')
98
99mgi = MakeGraph() #ugly hack to make the dot file of the graph after CTRL + C
100
101class SwircBot(irc.IRCClient):
102
103    nickname = BOT_NICKNAME
104
105    def __init__(self):
106        self.knownchannels = []
107        self.inchannels = []
108        self.knownnicks = []
109        self.cnicks = []
110        self.vchannels = [] # Channels we have to visit
111        self.wcount = 0
112        self.j = True
113
114        self.joinNextChannel()
115
116    def joinNextChannel(self):
117        print self.wcount
118        if not self.wcount and self.j:
119            try:
120                chan = self.vchannels.pop(0)
121                self.j = False
122                self.join(chan)
123            except IndexError:
124                pass
125
126        reactor.callLater(5, self.joinNextChannel)
127
128    def connectionMade(self):
129        irc.IRCClient.connectionMade(self)
130        self.graphmaker = mgi
131
132    def connectionLost(self, reason):
133        irc.IRCClient.connectionLost(self, reason)
134
135    def signedOn(self):
136        log.info('Signed On:')
137        self.join(self.factory.channel)
138
139    def joined(self, channel):
140        log.info('Joined: %s', channel)
141        self.j = True
142        self.cnicks = []
143        self.inchannels.append(channel)
144        if len(self.inchannels) > MAX_CURRENT_CHANNEL:
145            leaveChannel = self.inchannels.pop(0)
146            self.knownchannels.append(leaveChannel)
147            self.leave(leaveChannel, 'Bye')
148            log.info('Leaved channel: %s', leaveChannel)
149
150    def whois(self, nick):
151        self.sendLine('WHOIS %s' % (nick,))
152
153    def irc_RPL_NAMREPLY(self, prefix, params):
154        for nick in params[3].split():
155            if nick == BOT_NICKNAME:
156                continue
157            if nick[0] in '@+%':
158                nick = nick[1:]
159
160            self.cnicks.append(nick)
161
162        self.graphmaker.addNick(self.cnicks)
163
164    def irc_RPL_ENDOFNAMES(self, prefix, params):
165        for nick in self.cnicks:
166            if nick not in self.knownnicks:
167                self.whois(nick)
168                self.wcount += 1
169                time.sleep(0.2)
170
171    def irc_RPL_WHOISCHANNELS(self, prefix, params):
172        for channel in params[2].split():
173                if channel[0] in '@+%':
174                    channel = channel[1:]
175
176                if (channel not in self.knownchannels and
177                    channel not in self.inchannels and
178                    channel not in self.vchannels):
179                    self.vchannels.append(channel)
180
181    def irc_RPL_ENDOFWHOIS(self, prefix, params):
182        nick = params[1]
183        if nick not in self.knownnicks:
184            self.knownnicks.append(nick)
185        time.sleep(0.2)
186
187        self.wcount -= 1
188
189class SwircBotFactory(protocol.ClientFactory):
190
191    protocol = SwircBot
192
193    def __init__(self, channel):
194        self.channel = channel
195
196    def clientConnectionLost(self, connector, reason):
197        log.critical('Connection lost: %s', reason)
198        connector.connect()
199        #reactor.stop()
200
201    def clientConnectionFailed(self, connector, reason):
202        log.critical('Connection failed: %s', reason)
203        reactor.stop()
204
205if __name__ == '__main__':
206
207    f = SwircBotFactory(sys.argv[1])
208
209    reactor.connectTCP('irc.azzurra.org', 6667, f)
210
211    reactor.run()
212
213    mgi.saveDot('swirc.dot')
Note: See TracBrowser for help on using the repository browser.