17.12
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,293 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
IRC support for Instance Messenger.
|
||||
"""
|
||||
|
||||
from twisted.words.protocols import irc
|
||||
from twisted.words.im.locals import ONLINE
|
||||
from twisted.internet import defer, reactor, protocol
|
||||
from twisted.internet.defer import succeed
|
||||
from twisted.words.im import basesupport, interfaces, locals
|
||||
from zope.interface import implementer
|
||||
|
||||
|
||||
class IRCPerson(basesupport.AbstractPerson):
|
||||
|
||||
def imperson_whois(self):
|
||||
if self.account.client is None:
|
||||
raise locals.OfflineError
|
||||
self.account.client.sendLine("WHOIS %s" % self.name)
|
||||
|
||||
|
||||
### interface impl
|
||||
def isOnline(self):
|
||||
return ONLINE
|
||||
|
||||
|
||||
def getStatus(self):
|
||||
return ONLINE
|
||||
|
||||
|
||||
def setStatus(self,status):
|
||||
self.status=status
|
||||
self.chat.getContactsList().setContactStatus(self)
|
||||
|
||||
|
||||
def sendMessage(self, text, meta=None):
|
||||
if self.account.client is None:
|
||||
raise locals.OfflineError
|
||||
for line in text.split('\n'):
|
||||
if meta and meta.get("style", None) == "emote":
|
||||
self.account.client.ctcpMakeQuery(self.name,[('ACTION', line)])
|
||||
else:
|
||||
self.account.client.msg(self.name, line)
|
||||
return succeed(text)
|
||||
|
||||
|
||||
|
||||
@implementer(interfaces.IGroup)
|
||||
class IRCGroup(basesupport.AbstractGroup):
|
||||
def imgroup_testAction(self):
|
||||
pass
|
||||
|
||||
|
||||
def imtarget_kick(self, target):
|
||||
if self.account.client is None:
|
||||
raise locals.OfflineError
|
||||
reason = "for great justice!"
|
||||
self.account.client.sendLine("KICK #%s %s :%s" % (
|
||||
self.name, target.name, reason))
|
||||
|
||||
|
||||
### Interface Implementation
|
||||
def setTopic(self, topic):
|
||||
if self.account.client is None:
|
||||
raise locals.OfflineError
|
||||
self.account.client.topic(self.name, topic)
|
||||
|
||||
|
||||
def sendGroupMessage(self, text, meta={}):
|
||||
if self.account.client is None:
|
||||
raise locals.OfflineError
|
||||
if meta and meta.get("style", None) == "emote":
|
||||
self.account.client.ctcpMakeQuery(self.name,[('ACTION', text)])
|
||||
return succeed(text)
|
||||
#standard shmandard, clients don't support plain escaped newlines!
|
||||
for line in text.split('\n'):
|
||||
self.account.client.say(self.name, line)
|
||||
return succeed(text)
|
||||
|
||||
|
||||
def leave(self):
|
||||
if self.account.client is None:
|
||||
raise locals.OfflineError
|
||||
self.account.client.leave(self.name)
|
||||
self.account.client.getGroupConversation(self.name,1)
|
||||
|
||||
|
||||
|
||||
class IRCProto(basesupport.AbstractClientMixin, irc.IRCClient):
|
||||
def __init__(self, account, chatui, logonDeferred=None):
|
||||
basesupport.AbstractClientMixin.__init__(self, account, chatui,
|
||||
logonDeferred)
|
||||
self._namreplies={}
|
||||
self._ingroups={}
|
||||
self._groups={}
|
||||
self._topics={}
|
||||
|
||||
|
||||
def getGroupConversation(self, name, hide=0):
|
||||
name = name.lower()
|
||||
return self.chat.getGroupConversation(self.chat.getGroup(name, self),
|
||||
stayHidden=hide)
|
||||
|
||||
|
||||
def getPerson(self,name):
|
||||
return self.chat.getPerson(name, self)
|
||||
|
||||
|
||||
def connectionMade(self):
|
||||
# XXX: Why do I duplicate code in IRCClient.register?
|
||||
try:
|
||||
self.performLogin = True
|
||||
self.nickname = self.account.username
|
||||
self.password = self.account.password
|
||||
self.realname = "Twisted-IM user"
|
||||
|
||||
irc.IRCClient.connectionMade(self)
|
||||
|
||||
for channel in self.account.channels:
|
||||
self.joinGroup(channel)
|
||||
self.account._isOnline=1
|
||||
if self._logonDeferred is not None:
|
||||
self._logonDeferred.callback(self)
|
||||
self.chat.getContactsList()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def setNick(self,nick):
|
||||
self.name=nick
|
||||
self.accountName="%s (IRC)"%nick
|
||||
irc.IRCClient.setNick(self,nick)
|
||||
|
||||
|
||||
def kickedFrom(self, channel, kicker, message):
|
||||
"""
|
||||
Called when I am kicked from a channel.
|
||||
"""
|
||||
return self.chat.getGroupConversation(
|
||||
self.chat.getGroup(channel[1:], self), 1)
|
||||
|
||||
|
||||
def userKicked(self, kickee, channel, kicker, message):
|
||||
pass
|
||||
|
||||
|
||||
def noticed(self, username, channel, message):
|
||||
self.privmsg(username, channel, message, {"dontAutoRespond": 1})
|
||||
|
||||
|
||||
def privmsg(self, username, channel, message, metadata=None):
|
||||
if metadata is None:
|
||||
metadata = {}
|
||||
username = username.split('!',1)[0]
|
||||
if username==self.name: return
|
||||
if channel[0]=='#':
|
||||
group=channel[1:]
|
||||
self.getGroupConversation(group).showGroupMessage(username, message, metadata)
|
||||
return
|
||||
self.chat.getConversation(self.getPerson(username)).showMessage(message, metadata)
|
||||
|
||||
|
||||
def action(self,username,channel,emote):
|
||||
username = username.split('!',1)[0]
|
||||
if username==self.name: return
|
||||
meta={'style':'emote'}
|
||||
if channel[0]=='#':
|
||||
group=channel[1:]
|
||||
self.getGroupConversation(group).showGroupMessage(username, emote, meta)
|
||||
return
|
||||
self.chat.getConversation(self.getPerson(username)).showMessage(emote,meta)
|
||||
|
||||
|
||||
def irc_RPL_NAMREPLY(self,prefix,params):
|
||||
"""
|
||||
RPL_NAMREPLY
|
||||
>> NAMES #bnl
|
||||
<< :Arlington.VA.US.Undernet.Org 353 z3p = #bnl :pSwede Dan-- SkOyg AG
|
||||
"""
|
||||
group = params[2][1:].lower()
|
||||
users = params[3].split()
|
||||
for ui in range(len(users)):
|
||||
while users[ui][0] in ["@","+"]: # channel modes
|
||||
users[ui]=users[ui][1:]
|
||||
if group not in self._namreplies:
|
||||
self._namreplies[group]=[]
|
||||
self._namreplies[group].extend(users)
|
||||
for nickname in users:
|
||||
try:
|
||||
self._ingroups[nickname].append(group)
|
||||
except:
|
||||
self._ingroups[nickname]=[group]
|
||||
|
||||
|
||||
def irc_RPL_ENDOFNAMES(self,prefix,params):
|
||||
group=params[1][1:]
|
||||
self.getGroupConversation(group).setGroupMembers(self._namreplies[group.lower()])
|
||||
del self._namreplies[group.lower()]
|
||||
|
||||
|
||||
def irc_RPL_TOPIC(self,prefix,params):
|
||||
self._topics[params[1][1:]]=params[2]
|
||||
|
||||
|
||||
def irc_333(self,prefix,params):
|
||||
group=params[1][1:]
|
||||
self.getGroupConversation(group).setTopic(self._topics[group],params[2])
|
||||
del self._topics[group]
|
||||
|
||||
|
||||
def irc_TOPIC(self,prefix,params):
|
||||
nickname = prefix.split("!")[0]
|
||||
group = params[0][1:]
|
||||
topic = params[1]
|
||||
self.getGroupConversation(group).setTopic(topic,nickname)
|
||||
|
||||
|
||||
def irc_JOIN(self,prefix,params):
|
||||
nickname = prefix.split("!")[0]
|
||||
group = params[0][1:].lower()
|
||||
if nickname!=self.nickname:
|
||||
try:
|
||||
self._ingroups[nickname].append(group)
|
||||
except:
|
||||
self._ingroups[nickname]=[group]
|
||||
self.getGroupConversation(group).memberJoined(nickname)
|
||||
|
||||
|
||||
def irc_PART(self,prefix,params):
|
||||
nickname = prefix.split("!")[0]
|
||||
group = params[0][1:].lower()
|
||||
if nickname!=self.nickname:
|
||||
if group in self._ingroups[nickname]:
|
||||
self._ingroups[nickname].remove(group)
|
||||
self.getGroupConversation(group).memberLeft(nickname)
|
||||
|
||||
|
||||
def irc_QUIT(self,prefix,params):
|
||||
nickname = prefix.split("!")[0]
|
||||
if nickname in self._ingroups:
|
||||
for group in self._ingroups[nickname]:
|
||||
self.getGroupConversation(group).memberLeft(nickname)
|
||||
self._ingroups[nickname]=[]
|
||||
|
||||
|
||||
def irc_NICK(self, prefix, params):
|
||||
fromNick = prefix.split("!")[0]
|
||||
toNick = params[0]
|
||||
if fromNick not in self._ingroups:
|
||||
return
|
||||
for group in self._ingroups[fromNick]:
|
||||
self.getGroupConversation(group).memberChangedNick(fromNick, toNick)
|
||||
self._ingroups[toNick] = self._ingroups[fromNick]
|
||||
del self._ingroups[fromNick]
|
||||
|
||||
|
||||
def irc_unknown(self, prefix, command, params):
|
||||
pass
|
||||
|
||||
|
||||
# GTKIM calls
|
||||
def joinGroup(self,name):
|
||||
self.join(name)
|
||||
self.getGroupConversation(name)
|
||||
|
||||
|
||||
|
||||
@implementer(interfaces.IAccount)
|
||||
class IRCAccount(basesupport.AbstractAccount):
|
||||
gatewayType = "IRC"
|
||||
|
||||
_groupFactory = IRCGroup
|
||||
_personFactory = IRCPerson
|
||||
|
||||
def __init__(self, accountName, autoLogin, username, password, host, port,
|
||||
channels=''):
|
||||
basesupport.AbstractAccount.__init__(self, accountName, autoLogin,
|
||||
username, password, host, port)
|
||||
self.channels = [channel.strip() for channel in channels.split(',')]
|
||||
if self.channels == ['']:
|
||||
self.channels = []
|
||||
|
||||
|
||||
def _startLogOn(self, chatui):
|
||||
logonDeferred = defer.Deferred()
|
||||
cc = protocol.ClientCreator(reactor, IRCProto, self, chatui,
|
||||
logonDeferred)
|
||||
d = cc.connectTCP(self.host, self.port)
|
||||
d.addErrback(logonDeferred.errback)
|
||||
return logonDeferred
|
||||
@@ -0,0 +1,262 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""
|
||||
L{twisted.words} support for Instance Messenger.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import error
|
||||
from twisted.python import log
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.spread import pb
|
||||
|
||||
from twisted.words.im.locals import ONLINE, OFFLINE, AWAY
|
||||
|
||||
from twisted.words.im import basesupport, interfaces
|
||||
from zope.interface import implementer
|
||||
|
||||
|
||||
class TwistedWordsPerson(basesupport.AbstractPerson):
|
||||
"""I a facade for a person you can talk to through a twisted.words service.
|
||||
"""
|
||||
def __init__(self, name, wordsAccount):
|
||||
basesupport.AbstractPerson.__init__(self, name, wordsAccount)
|
||||
self.status = OFFLINE
|
||||
|
||||
def isOnline(self):
|
||||
return ((self.status == ONLINE) or
|
||||
(self.status == AWAY))
|
||||
|
||||
def getStatus(self):
|
||||
return self.status
|
||||
|
||||
def sendMessage(self, text, metadata):
|
||||
"""Return a deferred...
|
||||
"""
|
||||
if metadata:
|
||||
d=self.account.client.perspective.directMessage(self.name,
|
||||
text, metadata)
|
||||
d.addErrback(self.metadataFailed, "* "+text)
|
||||
return d
|
||||
else:
|
||||
return self.account.client.perspective.callRemote('directMessage',self.name, text)
|
||||
|
||||
def metadataFailed(self, result, text):
|
||||
print("result:",result,"text:",text)
|
||||
return self.account.client.perspective.directMessage(self.name, text)
|
||||
|
||||
def setStatus(self, status):
|
||||
self.status = status
|
||||
self.chat.getContactsList().setContactStatus(self)
|
||||
|
||||
@implementer(interfaces.IGroup)
|
||||
class TwistedWordsGroup(basesupport.AbstractGroup):
|
||||
def __init__(self, name, wordsClient):
|
||||
basesupport.AbstractGroup.__init__(self, name, wordsClient)
|
||||
self.joined = 0
|
||||
|
||||
def sendGroupMessage(self, text, metadata=None):
|
||||
"""Return a deferred.
|
||||
"""
|
||||
#for backwards compatibility with older twisted.words servers.
|
||||
if metadata:
|
||||
d=self.account.client.perspective.callRemote(
|
||||
'groupMessage', self.name, text, metadata)
|
||||
d.addErrback(self.metadataFailed, "* "+text)
|
||||
return d
|
||||
else:
|
||||
return self.account.client.perspective.callRemote('groupMessage',
|
||||
self.name, text)
|
||||
|
||||
def setTopic(self, text):
|
||||
self.account.client.perspective.callRemote(
|
||||
'setGroupMetadata',
|
||||
{'topic': text, 'topic_author': self.client.name},
|
||||
self.name)
|
||||
|
||||
def metadataFailed(self, result, text):
|
||||
print("result:",result,"text:",text)
|
||||
return self.account.client.perspective.callRemote('groupMessage',
|
||||
self.name, text)
|
||||
|
||||
def joining(self):
|
||||
self.joined = 1
|
||||
|
||||
def leaving(self):
|
||||
self.joined = 0
|
||||
|
||||
def leave(self):
|
||||
return self.account.client.perspective.callRemote('leaveGroup',
|
||||
self.name)
|
||||
|
||||
|
||||
|
||||
class TwistedWordsClient(pb.Referenceable, basesupport.AbstractClientMixin):
|
||||
"""In some cases, this acts as an Account, since it a source of text
|
||||
messages (multiple Words instances may be on a single PB connection)
|
||||
"""
|
||||
def __init__(self, acct, serviceName, perspectiveName, chatui,
|
||||
_logonDeferred=None):
|
||||
self.accountName = "%s (%s:%s)" % (acct.accountName, serviceName, perspectiveName)
|
||||
self.name = perspectiveName
|
||||
print("HELLO I AM A PB SERVICE", serviceName, perspectiveName)
|
||||
self.chat = chatui
|
||||
self.account = acct
|
||||
self._logonDeferred = _logonDeferred
|
||||
|
||||
def getPerson(self, name):
|
||||
return self.chat.getPerson(name, self)
|
||||
|
||||
def getGroup(self, name):
|
||||
return self.chat.getGroup(name, self)
|
||||
|
||||
def getGroupConversation(self, name):
|
||||
return self.chat.getGroupConversation(self.getGroup(name))
|
||||
|
||||
def addContact(self, name):
|
||||
self.perspective.callRemote('addContact', name)
|
||||
|
||||
def remote_receiveGroupMembers(self, names, group):
|
||||
print('received group members:', names, group)
|
||||
self.getGroupConversation(group).setGroupMembers(names)
|
||||
|
||||
def remote_receiveGroupMessage(self, sender, group, message, metadata=None):
|
||||
print('received a group message', sender, group, message, metadata)
|
||||
self.getGroupConversation(group).showGroupMessage(sender, message, metadata)
|
||||
|
||||
def remote_memberJoined(self, member, group):
|
||||
print('member joined', member, group)
|
||||
self.getGroupConversation(group).memberJoined(member)
|
||||
|
||||
def remote_memberLeft(self, member, group):
|
||||
print('member left')
|
||||
self.getGroupConversation(group).memberLeft(member)
|
||||
|
||||
def remote_notifyStatusChanged(self, name, status):
|
||||
self.chat.getPerson(name, self).setStatus(status)
|
||||
|
||||
def remote_receiveDirectMessage(self, name, message, metadata=None):
|
||||
self.chat.getConversation(self.chat.getPerson(name, self)).showMessage(message, metadata)
|
||||
|
||||
def remote_receiveContactList(self, clist):
|
||||
for name, status in clist:
|
||||
self.chat.getPerson(name, self).setStatus(status)
|
||||
|
||||
def remote_setGroupMetadata(self, dict_, groupName):
|
||||
if "topic" in dict_:
|
||||
self.getGroupConversation(groupName).setTopic(dict_["topic"], dict_.get("topic_author", None))
|
||||
|
||||
def joinGroup(self, name):
|
||||
self.getGroup(name).joining()
|
||||
return self.perspective.callRemote('joinGroup', name).addCallback(self._cbGroupJoined, name)
|
||||
|
||||
def leaveGroup(self, name):
|
||||
self.getGroup(name).leaving()
|
||||
return self.perspective.callRemote('leaveGroup', name).addCallback(self._cbGroupLeft, name)
|
||||
|
||||
def _cbGroupJoined(self, result, name):
|
||||
groupConv = self.chat.getGroupConversation(self.getGroup(name))
|
||||
groupConv.showGroupMessage("sys", "you joined")
|
||||
self.perspective.callRemote('getGroupMembers', name)
|
||||
|
||||
def _cbGroupLeft(self, result, name):
|
||||
print('left',name)
|
||||
groupConv = self.chat.getGroupConversation(self.getGroup(name), 1)
|
||||
groupConv.showGroupMessage("sys", "you left")
|
||||
|
||||
def connected(self, perspective):
|
||||
print('Connected Words Client!', perspective)
|
||||
if self._logonDeferred is not None:
|
||||
self._logonDeferred.callback(self)
|
||||
self.perspective = perspective
|
||||
self.chat.getContactsList()
|
||||
|
||||
|
||||
pbFrontEnds = {
|
||||
"twisted.words": TwistedWordsClient,
|
||||
"twisted.reality": None
|
||||
}
|
||||
|
||||
|
||||
@implementer(interfaces.IAccount)
|
||||
class PBAccount(basesupport.AbstractAccount):
|
||||
gatewayType = "PB"
|
||||
_groupFactory = TwistedWordsGroup
|
||||
_personFactory = TwistedWordsPerson
|
||||
|
||||
def __init__(self, accountName, autoLogin, username, password, host, port,
|
||||
services=None):
|
||||
"""
|
||||
@param username: The name of your PB Identity.
|
||||
@type username: string
|
||||
"""
|
||||
basesupport.AbstractAccount.__init__(self, accountName, autoLogin,
|
||||
username, password, host, port)
|
||||
self.services = []
|
||||
if not services:
|
||||
services = [('twisted.words', 'twisted.words', username)]
|
||||
for serviceType, serviceName, perspectiveName in services:
|
||||
self.services.append([pbFrontEnds[serviceType], serviceName,
|
||||
perspectiveName])
|
||||
|
||||
def logOn(self, chatui):
|
||||
"""
|
||||
@returns: this breaks with L{interfaces.IAccount}
|
||||
@returntype: DeferredList of L{interfaces.IClient}s
|
||||
"""
|
||||
# Overriding basesupport's implementation on account of the
|
||||
# fact that _startLogOn tends to return a deferredList rather
|
||||
# than a simple Deferred, and we need to do registerAccountClient.
|
||||
if (not self._isConnecting) and (not self._isOnline):
|
||||
self._isConnecting = 1
|
||||
d = self._startLogOn(chatui)
|
||||
d.addErrback(self._loginFailed)
|
||||
def registerMany(results):
|
||||
for success, result in results:
|
||||
if success:
|
||||
chatui.registerAccountClient(result)
|
||||
self._cb_logOn(result)
|
||||
else:
|
||||
log.err(result)
|
||||
d.addCallback(registerMany)
|
||||
return d
|
||||
else:
|
||||
raise error.ConnectionError("Connection in progress")
|
||||
|
||||
|
||||
def _startLogOn(self, chatui):
|
||||
print('Connecting...', end=' ')
|
||||
d = pb.getObjectAt(self.host, self.port)
|
||||
d.addCallbacks(self._cbConnected, self._ebConnected,
|
||||
callbackArgs=(chatui,))
|
||||
return d
|
||||
|
||||
def _cbConnected(self, root, chatui):
|
||||
print('Connected!')
|
||||
print('Identifying...', end=' ')
|
||||
d = pb.authIdentity(root, self.username, self.password)
|
||||
d.addCallbacks(self._cbIdent, self._ebConnected,
|
||||
callbackArgs=(chatui,))
|
||||
return d
|
||||
|
||||
def _cbIdent(self, ident, chatui):
|
||||
if not ident:
|
||||
print('falsely identified.')
|
||||
return self._ebConnected(Failure(Exception("username or password incorrect")))
|
||||
print('Identified!')
|
||||
dl = []
|
||||
for handlerClass, sname, pname in self.services:
|
||||
d = defer.Deferred()
|
||||
dl.append(d)
|
||||
handler = handlerClass(self, sname, pname, chatui, d)
|
||||
ident.callRemote('attach', sname, pname, handler).addCallback(handler.connected)
|
||||
return defer.DeferredList(dl)
|
||||
|
||||
def _ebConnected(self, error):
|
||||
print('Not connected.')
|
||||
return error
|
||||
|
||||
Reference in New Issue
Block a user