A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

141 строка
4.8 KiB

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2009-2012 by Ben Kurtovic <ben.kurtovic@verizon.net>
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22. import socket
  23. import threading
  24. __all__ = ["BrokenSocketException", "IRCConnection"]
  25. class BrokenSocketException(Exception):
  26. """A socket has broken, because it is not sending data.
  27. Raised by IRCConnection()._get().
  28. """
  29. pass
  30. class IRCConnection(object):
  31. """A class to interface with IRC."""
  32. def __init__(self, host, port, nick, ident, realname, logger):
  33. self.host = host
  34. self.port = port
  35. self.nick = nick
  36. self.ident = ident
  37. self.realname = realname
  38. self.logger = logger
  39. self.is_running = False
  40. # A lock to prevent us from sending two messages at once:
  41. self._lock = threading.Lock()
  42. def _connect(self):
  43. """Connect to our IRC server."""
  44. self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  45. try:
  46. self._sock.connect((self.host, self.port))
  47. except socket.error:
  48. self.logger.critical("Couldn't connect to IRC server", exc_info=1)
  49. exit(1)
  50. self._send("NICK {0}".format(self.nick))
  51. self._send("USER {0} {1} * :{2}".format(self.ident, self.host, self.realname))
  52. def _close(self):
  53. """Close our connection with the IRC server."""
  54. try:
  55. self._sock.shutdown(socket.SHUT_RDWR) # Shut down connection first
  56. except socket.error:
  57. pass # Ignore if the socket is already down
  58. self._sock.close()
  59. def _get(self, size=4096):
  60. """Receive (i.e. get) data from the server."""
  61. data = self._sock.recv(4096)
  62. if not data:
  63. # Socket isn't giving us any data, so it is dead or broken:
  64. raise BrokenSocketException()
  65. return data
  66. def _send(self, msg):
  67. """Send data to the server."""
  68. # Ensure that we only send one message at a time with a blocking lock:
  69. with self._lock:
  70. self._sock.sendall(msg + "\r\n")
  71. self.logger.debug(msg)
  72. def say(self, target, msg):
  73. """Send a private message to a target on the server."""
  74. msg = "PRIVMSG {0} :{1}".format(target, msg)
  75. self._send(msg)
  76. def reply(self, data, msg):
  77. """Send a private message as a reply to a user on the server."""
  78. msg = "\x02{0}\x0f: {1}".format(data.nick, msg)
  79. self.say(data.chan, msg)
  80. def action(self, target, msg):
  81. """Send a private message to a target on the server as an action."""
  82. msg = "\x01ACTION {0}\x01".format(msg)
  83. self.say(target, msg)
  84. def notice(self, target, msg):
  85. """Send a notice to a target on the server."""
  86. msg = "NOTICE {0} :{1}".format(target, msg)
  87. self._send(msg)
  88. def join(self, chan):
  89. """Join a channel on the server."""
  90. msg = "JOIN {0}".format(chan)
  91. self._send(msg)
  92. def part(self, chan):
  93. """Part from a channel on the server."""
  94. msg = "PART {0}".format(chan)
  95. self._send(msg)
  96. def mode(self, chan, level, msg):
  97. """Send a mode message to the server."""
  98. msg = "MODE {0} {1} {2}".format(chan, level, msg)
  99. self._send(msg)
  100. def pong(self, target):
  101. """Pong another entity on the server."""
  102. msg = "PONG {0}".format(target)
  103. self._send(msg)
  104. def loop(self):
  105. """Main loop for the IRC connection."""
  106. self.is_running = True
  107. read_buffer = ""
  108. while 1:
  109. try:
  110. read_buffer += self._get()
  111. except BrokenSocketException:
  112. self.is_running = False
  113. break
  114. lines = read_buffer.split("\n")
  115. read_buffer = lines.pop()
  116. for line in lines:
  117. self._process_message(line)
  118. if not self.is_running:
  119. break