JZTech101

Untitled

Dec 3rd, 2013
174
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.62 KB | None | 0 0
  1. ###
  2. # Copyright (c) 2002-2004, Jeremiah Fincher
  3. # Copyright (c) 2010, James McCoy
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are met:
  8. #
  9. # * Redistributions of source code must retain the above copyright notice,
  10. # this list of conditions, and the following disclaimer.
  11. # * Redistributions in binary form must reproduce the above copyright notice,
  12. # this list of conditions, and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. # * Neither the name of the author of this software nor the name of
  15. # contributors to this software may be used to endorse or promote products
  16. # derived from this software without specific prior written consent.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. ###
  30.  
  31. import time
  32.  
  33. import supybot.conf as conf
  34. import supybot.utils as utils
  35. import supybot.world as world
  36. from supybot.commands import *
  37. import supybot.irclib as irclib
  38. import supybot.ircmsgs as ircmsgs
  39. import supybot.ircutils as ircutils
  40. import supybot.callbacks as callbacks
  41. from supybot.utils.structures import MultiSet, TimeoutQueue
  42. from supybot.i18n import PluginInternationalization, internationalizeDocstring
  43. _ = PluginInternationalization('Relay')
  44.  
  45. class Relay(callbacks.Plugin):
  46. noIgnore = True
  47. def __init__(self, irc):
  48. self.__parent = super(Relay, self)
  49. self.__parent.__init__(irc)
  50. self._whois = {}
  51. self.lastmsg = {}
  52. self.ircstates = {}
  53. self.queuedTopics = MultiSet()
  54. self.lastRelayMsgs = ircutils.IrcDict()
  55.  
  56. def __call__(self, irc, msg):
  57. try:
  58. irc = self._getRealIrc(irc)
  59. if irc not in self.ircstates:
  60. self._addIrc(irc)
  61. self.ircstates[irc].addMsg(irc, self.lastmsg[irc])
  62. finally:
  63. self.lastmsg[irc] = msg
  64. self.__parent.__call__(irc, msg)
  65.  
  66. def do376(self, irc, msg):
  67. networkGroup = conf.supybot.networks.get(irc.network)
  68. for channel in self.registryValue('channels'):
  69. if self.registryValue('channels.joinOnAllNetworks', channel):
  70. if channel not in irc.state.channels:
  71. irc.queueMsg(networkGroup.channels.join(channel))
  72. do377 = do422 = do376
  73.  
  74. def _getRealIrc(self, irc):
  75. if isinstance(irc, irclib.Irc):
  76. return irc
  77. else:
  78. return irc.getRealIrc()
  79.  
  80. def _getIrcName(self, irc):
  81. # We should allow abbreviations at some point.
  82. return irc.network
  83.  
  84. def _addIrc(self, irc):
  85. # Let's just be extra-special-careful here.
  86. if irc not in self.ircstates:
  87. self.ircstates[irc] = irclib.IrcState()
  88. if irc not in self.lastmsg:
  89. self.lastmsg[irc] = ircmsgs.ping('this is just a fake message')
  90. if irc.afterConnect:
  91. # We've probably been reloaded. Let's send some messages to get
  92. # our IrcState objects up to current.
  93. for channel in self.registryValue('channels'):
  94. irc.queueMsg(ircmsgs.who(channel))
  95. irc.queueMsg(ircmsgs.names(channel))
  96.  
  97. @internationalizeDocstring
  98. def join(self, irc, msg, args, channel):
  99. """[<channel>]
  100.  
  101. Starts relaying between the channel <channel> on all networks. If on a
  102. network the bot isn't in <channel>, he'll join. This commands is
  103. required even if the bot is in the channel on both networks; he won't
  104. relay between those channels unless he's told to join both
  105. channels. If <channel> is not given, starts relaying on the channel
  106. the message was sent in.
  107. """
  108. self.registryValue('channels').add(channel)
  109. for otherIrc in world.ircs:
  110. if channel not in otherIrc.state.channels:
  111. networkGroup = conf.supybot.networks.get(otherIrc.network)
  112. otherIrc.queueMsg(networkGroup.channels.join(channel))
  113. irc.replySuccess()
  114. join = wrap(join, ['channel', 'admin'])
  115.  
  116. @internationalizeDocstring
  117. def part(self, irc, msg, args, channel):
  118. """<channel>
  119.  
  120. Ceases relaying between the channel <channel> on all networks. The bot
  121. will part from the channel on all networks in which it is on the
  122. channel.
  123. """
  124. self.registryValue('channels').discard(channel)
  125. for otherIrc in world.ircs:
  126. if channel in otherIrc.state.channels:
  127. otherIrc.queueMsg(ircmsgs.part(channel))
  128. irc.replySuccess()
  129. part = wrap(part, ['channel', 'admin'])
  130.  
  131. @internationalizeDocstring
  132. def nicks(self, irc, msg, args, channel):
  133. """[<channel>]
  134.  
  135. Returns the nicks of the people in the channel on the various networks
  136. the bot is connected to. <channel> is only necessary if the message
  137. isn't sent on the channel itself.
  138. """
  139. realIrc = self._getRealIrc(irc)
  140. if channel not in self.registryValue('channels'):
  141. irc.error(format('I\'m not relaying in %s.', channel))
  142. return
  143. users = []
  144. for otherIrc in world.ircs:
  145. network = self._getIrcName(otherIrc)
  146. ops = []
  147. halfops = []
  148. voices = []
  149. usersS = []
  150. if network != self._getIrcName(realIrc):
  151. try:
  152. Channel = otherIrc.state.channels[channel]
  153. except KeyError:
  154. users.append(format('(not in %s on %s)',channel,network))
  155. continue
  156. numUsers = 0
  157. for s in Channel.users:
  158. s = s.strip()
  159. if not s:
  160. continue
  161. numUsers += 1
  162. if s in Channel.ops:
  163. ops.append('@' + s)
  164. elif s in Channel.halfops:
  165. halfops.append('%' + s)
  166. elif s in Channel.voices:
  167. voices.append('+' + s)
  168. else:
  169. usersS.append(s)
  170. utils.sortBy(ircutils.toLower, ops)
  171. utils.sortBy(ircutils.toLower, voices)
  172. utils.sortBy(ircutils.toLower, halfops)
  173. utils.sortBy(ircutils.toLower, usersS)
  174. usersS = ', '.join(filter(None, map(', '.join,
  175. (ops,halfops,voices,usersS))))
  176. users.append(format('%s (%i): %s',
  177. ircutils.bold(network), numUsers, usersS))
  178. users.sort()
  179. irc.reply('; '.join(users))
  180. nicks = wrap(nicks, ['channel'])
  181.  
  182. def do311(self, irc, msg):
  183. irc = self._getRealIrc(irc)
  184. nick = ircutils.toLower(msg.args[1])
  185. if (irc, nick) not in self._whois:
  186. return
  187. else:
  188. self._whois[(irc, nick)][-1][msg.command] = msg
  189.  
  190. # These are all sent by a WHOIS response.
  191. do301 = do311
  192. do312 = do311
  193. do317 = do311
  194. do319 = do311
  195. do320 = do311
  196.  
  197. def do318(self, irc, msg):
  198. irc = self._getRealIrc(irc)
  199. nick = msg.args[1]
  200. loweredNick = ircutils.toLower(nick)
  201. if (irc, loweredNick) not in self._whois:
  202. return
  203. (replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
  204. hostmask = '@'.join(d['311'].args[2:4])
  205. user = d['311'].args[-1]
  206. if '319' in d:
  207. channels = d['319'].args[-1].split()
  208. ops = []
  209. voices = []
  210. normal = []
  211. halfops = []
  212. for channel in channels:
  213. if channel.startswith('@'):
  214. ops.append(channel[1:])
  215. elif channel.startswith('%'):
  216. halfops.append(channel[1:])
  217. elif channel.startswith('+'):
  218. voices.append(channel[1:])
  219. else:
  220. normal.append(channel)
  221. L = []
  222. if ops:
  223. L.append(format(_('is an op on %L'), ops))
  224. if halfops:
  225. L.append(format(_('is a halfop on %L'), halfups))
  226. if voices:
  227. L.append(format(_('is voiced on %L'), voices))
  228. if normal:
  229. if L:
  230. L.append(format(_('is also on %L'), normal))
  231. else:
  232. L.append(format(_('is on %L'), normal))
  233. else:
  234. L = [_('isn\'t on any non-secret channels')]
  235. channels = format('%L', L)
  236. if '317' in d:
  237. idle = utils.timeElapsed(d['317'].args[2])
  238. signon = time.strftime(conf.supybot.reply.format.time(),
  239. time.localtime(float(d['317'].args[3])))
  240. else:
  241. idle = _('<unknown>')
  242. signon = _('<unknown>')
  243. if '312' in d:
  244. server = d['312'].args[2]
  245. else:
  246. server = _('<unknown>')
  247. if '301' in d:
  248. away = format(_(' %s is away: %s.'), nick, d['301'].args[2])
  249. else:
  250. away = ''
  251. if '320' in d:
  252. if d['320'].args[2]:
  253. identify = _(' identified')
  254. else:
  255. identify = ''
  256. else:
  257. identify = ''
  258. s = format(_('%s (%s) has been%s on server %s since %s (idle for %s) '
  259. 'and %s.%s'),
  260. user, hostmask, identify, server, signon, idle,
  261. channels, away)
  262. replyIrc.reply(s)
  263. del self._whois[(irc, loweredNick)]
  264.  
  265. def do402(self, irc, msg):
  266. irc = self._getRealIrc(irc)
  267. nick = msg.args[1]
  268. loweredNick = ircutils.toLower(nick)
  269. if (irc, loweredNick) not in self._whois:
  270. return
  271. (replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
  272. del self._whois[(irc, loweredNick)]
  273. s = format(_('There is no %s on %s.'), nick, self._getIrcName(irc))
  274. replyIrc.reply(s)
  275.  
  276. do401 = do402
  277.  
  278. def simpleHash(self, s):
  279. colors = ["\x0305", "\x0304", "\x0303", "\x0309", "\x0302", "\x0312",
  280. "\x0306", "\x0313", "\x0310", "\x0311", "\x0307"]
  281. num = 0
  282. for i in s:
  283. num += ord(i)
  284. num = num % 11
  285. return colors[num]
  286.  
  287. def _formatPrivmsg(self, nick, network, msg):
  288. channel = msg.args[0]
  289. if self.registryValue('includeNetwork', channel):
  290. network="/" + network
  291. networkcolor = self.simplehash(network)
  292. nickcolor = self.simplehash(nick)
  293. else:
  294. network = ''
  295. sas="/"
  296. # colorize nicks
  297. color = self.registryValue('color', channel) # Also used further down.
  298. if color:
  299. nick = ircutils.IrcString(nick)
  300. newnick = ircutils.mircColor(nick, *ircutils.canonicalColor(nick))
  301. colors = ircutils.canonicalColor(nick, shift=4)
  302. nick = newnick
  303. if ircmsgs.isAction(msg):
  304. if color:
  305. t = ircutils.mircColor('*', *colors)
  306. else:
  307. t = '*'
  308. s = format(nickcolor, t, nick, networkcolor, network, ircmsgs.unAction(msg))
  309. else:
  310. if color:
  311. lt = ircutils.mircColor('<', *colors)
  312. gt = ircutils.mircColor('>', *colors)
  313. else:
  314. lt = '<'
  315. gt = '>'
  316. s = format(nickcolor, lt, nick,networkcolor, network, gt, msg.args[1])
  317. return s
  318.  
  319. def _sendToOthers(self, irc, msg):
  320. assert msg.command in ('PRIVMSG', 'NOTICE', 'TOPIC')
  321. for otherIrc in world.ircs:
  322. if otherIrc != irc and not otherIrc.zombie:
  323. if msg.args[0] in otherIrc.state.channels:
  324. msg.tag('relayedMsg')
  325. otherIrc.queueMsg(msg)
  326.  
  327. def _checkRelayMsg(self, msg):
  328. channel = msg.args[0]
  329. if channel in self.lastRelayMsgs:
  330. q = self.lastRelayMsgs[channel]
  331. unformatted = ircutils.stripFormatting(msg.args[1])
  332. normalized = utils.str.normalizeWhitespace(unformatted)
  333. for s in q:
  334. if s in normalized:
  335. return True
  336. return False
  337.  
  338. def _punishRelayers(self, msg):
  339. assert self._checkRelayMsg(msg), 'Punishing without checking.'
  340. who = msg.prefix
  341. channel = msg.args[0]
  342. def notPunishing(irc, s, *args):
  343. self.log.info('Not punishing %s in %s on %s: %s.',
  344. msg.prefix, channel, irc.network, s, *args)
  345. for irc in world.ircs:
  346. if channel in irc.state.channels:
  347. if irc.nick in irc.state.channels[channel].ops:
  348. if who in irc.state.channels[channel].bans:
  349. notPunishing(irc, 'already banned')
  350. else:
  351. self.log.info('Punishing %s in %s on %s for relaying.',
  352. who, channel, irc.network)
  353. irc.sendMsg(ircmsgs.ban(channel, who))
  354. kmsg = _('You seem to be relaying, punk.')
  355. irc.sendMsg(ircmsgs.kick(channel, msg.nick, kmsg))
  356. else:
  357. notPunishing(irc, 'not opped')
  358.  
  359. def doPrivmsg(self, irc, msg):
  360. if ircmsgs.isCtcp(msg) and not ircmsgs.isAction(msg):
  361. return
  362. (channel, text) = msg.args
  363. if irc.isChannel(channel):
  364. irc = self._getRealIrc(irc)
  365. if channel not in self.registryValue('channels'):
  366. return
  367. ignores = self.registryValue('ignores', channel)
  368. for ignore in ignores:
  369. if ircutils.hostmaskPatternEqual(ignore, msg.prefix):
  370. self.log.debug('Refusing to relay %s, ignored by %s.',
  371. msg.prefix, ignore)
  372. return
  373. # Let's try to detect other relay bots.
  374. if self._checkRelayMsg(msg):
  375. if self.registryValue('punishOtherRelayBots', channel):
  376. self._punishRelayers(msg)
  377. # Either way, we don't relay the message.
  378. else:
  379. self.log.warning('Refusing to relay message from %s, '
  380. 'it appears to be a relay message.',
  381. msg.prefix)
  382. else:
  383. network = self._getIrcName(irc)
  384. s = self._formatPrivmsg(msg.nick, network, msg)
  385. m = self._msgmaker(channel, s)
  386. self._sendToOthers(irc, m)
  387.  
  388. def _msgmaker(self, target, s):
  389. msg = dynamic.msg
  390. channel = dynamic.channel
  391. if self.registryValue('noticeNonPrivmsgs', dynamic.channel) and \
  392. msg.command != 'PRIVMSG':
  393. return ircmsgs.notice(target, s)
  394. else:
  395. return ircmsgs.privmsg(target, s)
  396.  
  397. def doJoin(self, irc, msg):
  398. irc = self._getRealIrc(irc)
  399. channel = msg.args[0]
  400. if channel not in self.registryValue('channels'):
  401. return
  402. network = self._getIrcName(irc)
  403. if self.registryValue('hostmasks', channel):
  404. hostmask = format(' (%s)', msg.prefix.split('!')[1])
  405. else:
  406. hostmask = ''
  407. s = format(_('%s%s has joined on %s'), msg.nick, hostmask, network)
  408. m = self._msgmaker(channel, s)
  409. self._sendToOthers(irc, m)
  410.  
  411. def doPart(self, irc, msg):
  412. irc = self._getRealIrc(irc)
  413. channel = msg.args[0]
  414. if channel not in self.registryValue('channels'):
  415. return
  416. network = self._getIrcName(irc)
  417. if self.registryValue('hostmasks', channel):
  418. hostmask = format(' (%s)', msg.prefix.split('!')[1])
  419. else:
  420. hostmask = ''
  421. if len(msg.args) > 1:
  422. s = format(_('%s%s has left on %s (%s)'),
  423. msg.nick, hostmask, network, msg.args[1])
  424. else:
  425. s = format(_('%s%s has left on %s'), msg.nick, hostmask, network)
  426. m = self._msgmaker(channel, s)
  427. self._sendToOthers(irc, m)
  428.  
  429. def doMode(self, irc, msg):
  430. irc = self._getRealIrc(irc)
  431. channel = msg.args[0]
  432. if channel not in self.registryValue('channels'):
  433. return
  434. network = self._getIrcName(irc)
  435. s = format(_('mode change by %s on %s: %s'),
  436. msg.nick, network, ' '.join(msg.args[1:]))
  437. m = self._msgmaker(channel, s)
  438. self._sendToOthers(irc, m)
  439.  
  440. def doKick(self, irc, msg):
  441. irc = self._getRealIrc(irc)
  442. channel = msg.args[0]
  443. if channel not in self.registryValue('channels'):
  444. return
  445. network = self._getIrcName(irc)
  446. if len(msg.args) == 3:
  447. s = format(_('%s was kicked by %s on %s (%s)'),
  448. msg.args[1], msg.nick, network, msg.args[2])
  449. else:
  450. s = format(_('%s was kicked by %s on %s'),
  451. msg.args[1], msg.nick, network)
  452. m = self._msgmaker(channel, s)
  453. self._sendToOthers(irc, m)
  454.  
  455. def doNick(self, irc, msg):
  456. irc = self._getRealIrc(irc)
  457. newNick = msg.args[0]
  458. network = self._getIrcName(irc)
  459. s = format(_('nick change by %s to %s on %s'), msg.nick,newNick,network)
  460. for channel in self.registryValue('channels'):
  461. if channel in irc.state.channels:
  462. if newNick in irc.state.channels[channel].users:
  463. m = self._msgmaker(channel, s)
  464. self._sendToOthers(irc, m)
  465.  
  466. def doTopic(self, irc, msg):
  467. irc = self._getRealIrc(irc)
  468. (channel, newTopic) = msg.args
  469. if channel not in self.registryValue('channels'):
  470. return
  471. network = self._getIrcName(irc)
  472. if self.registryValue('topicSync', channel):
  473. m = ircmsgs.topic(channel, newTopic)
  474. for otherIrc in world.ircs:
  475. if irc != otherIrc:
  476. try:
  477. if otherIrc.state.getTopic(channel) != newTopic:
  478. if (otherIrc, newTopic) not in self.queuedTopics:
  479. self.queuedTopics.add((otherIrc, newTopic))
  480. otherIrc.queueMsg(m)
  481. else:
  482. self.queuedTopics.remove((otherIrc, newTopic))
  483.  
  484. except KeyError:
  485. self.log.warning('Not on %s on %s, '
  486. 'can\'t sync topics.',
  487. channel, otherIrc.network)
  488. else:
  489. s = format(_('topic change by %s on %s: %s'),
  490. msg.nick, network, newTopic)
  491. m = self._msgmaker(channel, s)
  492. self._sendToOthers(irc, m)
  493.  
  494. def doQuit(self, irc, msg):
  495. irc = self._getRealIrc(irc)
  496. network = self._getIrcName(irc)
  497. if msg.args:
  498. s = format(_('%s has quit %s (%s)'), msg.nick, network, msg.args[0])
  499. else:
  500. s = format(_('%s has quit %s.'), msg.nick, network)
  501. for channel in self.registryValue('channels'):
  502. if channel in self.ircstates[irc].channels:
  503. if msg.nick in self.ircstates[irc].channels[channel].users:
  504. m = self._msgmaker(channel, s)
  505. self._sendToOthers(irc, m)
  506.  
  507. def doError(self, irc, msg):
  508. irc = self._getRealIrc(irc)
  509. network = self._getIrcName(irc)
  510. s = format(_('disconnected from %s: %s'), network, msg.args[0])
  511. for channel in self.registryValue('channels'):
  512. m = self._msgmaker(channel, s)
  513. self._sendToOthers(irc, m)
  514.  
  515. def outFilter(self, irc, msg):
  516. irc = self._getRealIrc(irc)
  517. if msg.command == 'PRIVMSG':
  518. if msg.relayedMsg:
  519. self._addRelayMsg(msg)
  520. else:
  521. channel = msg.args[0]
  522. if channel in self.registryValue('channels'):
  523. network = self._getIrcName(irc)
  524. s = self._formatPrivmsg(irc.nick, network, msg)
  525. relayMsg = self._msgmaker(channel, s)
  526. self._sendToOthers(irc, relayMsg)
  527. return msg
  528.  
  529. def _addRelayMsg(self, msg):
  530. channel = msg.args[0]
  531. if channel in self.lastRelayMsgs:
  532. q = self.lastRelayMsgs[channel]
  533. else:
  534. q = TimeoutQueue(60) # XXX Make this configurable.
  535. self.lastRelayMsgs[channel] = q
  536. unformatted = ircutils.stripFormatting(msg.args[1])
  537. normalized = utils.str.normalizeWhitespace(unformatted)
  538. q.enqueue(normalized)
  539.  
  540.  
  541. Class = Relay
  542.  
  543. # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
Advertisement
Add Comment
Please, Sign In to add comment