1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 IRC support for Instance Messenger.
10 from twisted.words.protocols import irc
11 from twisted.words.im.locals import ONLINE
12 from twisted.internet import defer, reactor, protocol
13 from twisted.internet.defer import succeed
14 from twisted.words.im import basesupport, interfaces, locals
15 from zope.interface import implements
18 class IRCPerson(basesupport.AbstractPerson):
20 def imperson_whois(self):
21 if self.account.client is None:
22 raise locals.OfflineError
23 self.account.client.sendLine("WHOIS %s" % self.name)
33 def setStatus(self,status):
35 self.chat.getContactsList().setContactStatus(self)
37 def sendMessage(self, text, meta=None):
38 if self.account.client is None:
39 raise locals.OfflineError
40 for line in string.split(text, '\n'):
41 if meta and meta.get("style", None) == "emote":
42 self.account.client.ctcpMakeQuery(self.name,[('ACTION', line)])
44 self.account.client.msg(self.name, line)
47 class IRCGroup(basesupport.AbstractGroup):
49 implements(interfaces.IGroup)
51 def imgroup_testAction(self):
54 def imtarget_kick(self, target):
55 if self.account.client is None:
56 raise locals.OfflineError
57 reason = "for great justice!"
58 self.account.client.sendLine("KICK #%s %s :%s" % (
59 self.name, target.name, reason))
61 ### Interface Implementation
63 def setTopic(self, topic):
64 if self.account.client is None:
65 raise locals.OfflineError
66 self.account.client.topic(self.name, topic)
68 def sendGroupMessage(self, text, meta={}):
69 if self.account.client is None:
70 raise locals.OfflineError
71 if meta and meta.get("style", None) == "emote":
72 self.account.client.me(self.name,text)
74 #standard shmandard, clients don't support plain escaped newlines!
75 for line in string.split(text, '\n'):
76 self.account.client.say(self.name, line)
80 if self.account.client is None:
81 raise locals.OfflineError
82 self.account.client.leave(self.name)
83 self.account.client.getGroupConversation(self.name,1)
86 class IRCProto(basesupport.AbstractClientMixin, irc.IRCClient):
87 def __init__(self, account, chatui, logonDeferred=None):
88 basesupport.AbstractClientMixin.__init__(self, account, chatui,
95 def getGroupConversation(self, name, hide=0):
96 name=string.lower(name)
97 return self.chat.getGroupConversation(self.chat.getGroup(name, self),
100 def getPerson(self,name):
101 return self.chat.getPerson(name, self)
103 def connectionMade(self):
104 # XXX: Why do I duplicate code in IRCClient.register?
106 if self.account.password:
107 self.sendLine("PASS :%s" % self.account.password)
108 self.setNick(self.account.username)
109 self.sendLine("USER %s foo bar :Twisted-IM user" % (
110 self.account.username,))
111 for channel in self.account.channels:
112 self.joinGroup(channel)
113 self.account._isOnline=1
114 if self._logonDeferred is not None:
115 self._logonDeferred.callback(self)
116 self.chat.getContactsList()
119 traceback.print_exc()
121 def setNick(self,nick):
123 self.accountName="%s (IRC)"%nick
124 irc.IRCClient.setNick(self,nick)
126 def kickedFrom(self, channel, kicker, message):
128 Called when I am kicked from a channel.
130 return self.chat.getGroupConversation(
131 self.chat.getGroup(channel[1:], self), 1)
133 def userKicked(self, kickee, channel, kicker, message):
136 def noticed(self, username, channel, message):
137 self.privmsg(username, channel, message, {"dontAutoRespond": 1})
139 def privmsg(self, username, channel, message, metadata=None):
142 username=string.split(username,'!',1)[0]
143 if username==self.name: return
146 self.getGroupConversation(group).showGroupMessage(username, message, metadata)
148 self.chat.getConversation(self.getPerson(username)).showMessage(message, metadata)
150 def action(self,username,channel,emote):
151 username=string.split(username,'!',1)[0]
152 if username==self.name: return
153 meta={'style':'emote'}
156 self.getGroupConversation(group).showGroupMessage(username, emote, meta)
158 self.chat.getConversation(self.getPerson(username)).showMessage(emote,meta)
160 def irc_RPL_NAMREPLY(self,prefix,params):
164 << :Arlington.VA.US.Undernet.Org 353 z3p = #bnl :pSwede Dan-- SkOyg AG
166 group=string.lower(params[2][1:])
167 users=string.split(params[3])
168 for ui in range(len(users)):
169 while users[ui][0] in ["@","+"]: # channel modes
170 users[ui]=users[ui][1:]
171 if not self._namreplies.has_key(group):
172 self._namreplies[group]=[]
173 self._namreplies[group].extend(users)
174 for nickname in users:
176 self._ingroups[nickname].append(group)
178 self._ingroups[nickname]=[group]
180 def irc_RPL_ENDOFNAMES(self,prefix,params):
182 self.getGroupConversation(group).setGroupMembers(self._namreplies[string.lower(group)])
183 del self._namreplies[string.lower(group)]
185 def irc_RPL_TOPIC(self,prefix,params):
186 self._topics[params[1][1:]]=params[2]
188 def irc_333(self,prefix,params):
190 self.getGroupConversation(group).setTopic(self._topics[group],params[2])
191 del self._topics[group]
193 def irc_TOPIC(self,prefix,params):
194 nickname = string.split(prefix,"!")[0]
195 group = params[0][1:]
197 self.getGroupConversation(group).setTopic(topic,nickname)
199 def irc_JOIN(self,prefix,params):
200 nickname=string.split(prefix,"!")[0]
201 group=string.lower(params[0][1:])
202 if nickname!=self.nickname:
204 self._ingroups[nickname].append(group)
206 self._ingroups[nickname]=[group]
207 self.getGroupConversation(group).memberJoined(nickname)
209 def irc_PART(self,prefix,params):
210 nickname=string.split(prefix,"!")[0]
211 group=string.lower(params[0][1:])
212 if nickname!=self.nickname:
213 if group in self._ingroups[nickname]:
214 self._ingroups[nickname].remove(group)
215 self.getGroupConversation(group).memberLeft(nickname)
217 def irc_QUIT(self,prefix,params):
218 nickname=string.split(prefix,"!")[0]
219 if self._ingroups.has_key(nickname):
220 for group in self._ingroups[nickname]:
221 self.getGroupConversation(group).memberLeft(nickname)
222 self._ingroups[nickname]=[]
224 def irc_NICK(self, prefix, params):
225 fromNick = string.split(prefix, "!")[0]
227 if not self._ingroups.has_key(fromNick):
229 for group in self._ingroups[fromNick]:
230 self.getGroupConversation(group).memberChangedNick(fromNick, toNick)
231 self._ingroups[toNick] = self._ingroups[fromNick]
232 del self._ingroups[fromNick]
234 def irc_unknown(self, prefix, command, params):
238 def joinGroup(self,name):
240 self.getGroupConversation(name)
242 class IRCAccount(basesupport.AbstractAccount):
243 implements(interfaces.IAccount)
246 _groupFactory = IRCGroup
247 _personFactory = IRCPerson
249 def __init__(self, accountName, autoLogin, username, password, host, port,
251 basesupport.AbstractAccount.__init__(self, accountName, autoLogin,
252 username, password, host, port)
253 self.channels = map(string.strip,string.split(channels,','))
254 if self.channels == ['']:
257 def _startLogOn(self, chatui):
258 logonDeferred = defer.Deferred()
259 cc = protocol.ClientCreator(reactor, IRCProto, self, chatui,
261 d = cc.connectTCP(self.host, self.port)
262 d.addErrback(logonDeferred.errback)