replace.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. ##NEEDS
  2. #adding a bold character for '<user> MEANT so say'
  3. import requests, sys
  4. from logger import Logger
  5. from event import Event
  6. if sys.version_info > (3, 0, 0):
  7. try:
  8. from .basemodule import BaseModule
  9. except (ImportError, SystemError):
  10. from modules.basemodule import BaseModule
  11. else:
  12. try:
  13. from basemodule import BaseModule
  14. except (ImportError, SystemError):
  15. from modules.basemodule import BaseModule
  16. class Replace(BaseModule):
  17. def post_init(self):
  18. if not 'replace' in self.bot.mem_store:
  19. self.bot.mem_store['replace'] = {}
  20. replace = Event("__.r__")
  21. replace.define(msg_definition=".*")
  22. replace.subscribe(self)
  23. self.bot.register_event(replace, self)
  24. self.help = ".r <search string> | <replacement text> OR s/<search string>/<replacement string>"
  25. self.MAX_BUFFER_SIZE = 300
  26. self.MAX_HISTORY_SIZE = 10
  27. def add_buffer(self, event=None, debug=False):
  28. """Takes a channel name and line passed to it and stores them in the bot's mem_store dict
  29. for future access. The dict will have channel as key. The value to that key will be a list
  30. of formatted lines of activity.
  31. If the buffer size is not yet exceeded, lines are just added. If the buffer
  32. is maxed out, the oldest line is removed and newest one inserted at the beginning.
  33. """
  34. if debug:
  35. print("Line: " + event.line)
  36. print("Verb: " + event.verb)
  37. print("Channel: " + event.channel)
  38. print("")
  39. if not event:
  40. return
  41. #there are certain things we want to record in history, like nick changes and quits
  42. #these often add to the humor of a quote. however, these are not specific to a channel
  43. #in IRC and our bot does not maintain a userlist per channel. Therefore, when nick
  44. #changes and quits occur, we will add them to every buffer. This is not technically
  45. #correct behavior and could very well lead to quits/nick changes that are not visible
  46. #showing up in a quote, but it's the best we can do at the moment
  47. if not event.channel:
  48. #discard events with unwanted verbs
  49. if event.verb not in ["QUIT", "NICK"]:
  50. return
  51. try:
  52. for chan in list(self.bot.mem_store['replace'].keys()):
  53. if len(self.bot.mem_store['replace'][chan]) >= self.MAX_BUFFER_SIZE:
  54. self.bot.mem_store['replace'][chan].pop()
  55. line = self.format_line(event)
  56. if line:
  57. self.bot.mem_store['replace'][chan].insert(0, line)
  58. except (KeyError, IndexError):
  59. self.bot.logger.write(Logger.WARNING, line="couldn't find channel in modules.replace")
  60. self.bot.logger.write(Logger.WARNING, line="Replace add_buffer() error when no event channel")
  61. #now we continue with normal, per channel line addition
  62. #create a dictionary associating the channel with an empty list if it doesn't exist yet
  63. # END if not event.channel:
  64. else:
  65. if event.channel not in self.bot.mem_store['replace']:
  66. self.bot.mem_store['replace'][event.channel] = []
  67. try:
  68. #check for the length of the buffer. if it's too long, pop the last item
  69. if len(self.bot.mem_store['replace'][event.channel]) >= self.MAX_BUFFER_SIZE:
  70. self.bot.mem_store['replace'][event.channel].pop()
  71. #get a line by passing event to format_line
  72. #insert the line into the first position in the list
  73. line = self.format_line(event)
  74. if line:
  75. self.bot.mem_store['replace'][event.channel].insert(0, line)
  76. except IndexError:
  77. self.bot.logger.write(Logger.WARNING, line="Replace add_buffer() error. Couldn't access the list index.")
  78. def format_line(self, event):
  79. """Takes an event and formats a string appropriate for quotation from it"""
  80. #format all strings based on the verb
  81. if event.verb == "":
  82. return ''
  83. elif event.verb == "PRIVMSG":
  84. #special formatting for ACTION strings
  85. if event.msg.startswith('\001ACTION'):
  86. #strip out the word ACTION from the msg
  87. return ' * %s %s\n' % (event.user, event.msg[7:])
  88. else:
  89. return '<%s> %s\n' % (event.user, event.msg)
  90. else:
  91. #no matching verbs found. just ignore the line
  92. return ''
  93. def get_replacement_message(self, channel=None, find_msg=''):
  94. """Looks through the mem_store to find the most recent message containing find_msg"""
  95. if not channel:
  96. self.bot.logger.write(Logger.WARNING, line="couldn't find channel")
  97. return None
  98. #must have at least one msg to search for and channel to look it up in
  99. if len(find_msg) == 0 or not channel:
  100. self.bot.logger.write(Logger.WARNING, line="find_msg is empty")
  101. return None
  102. #search for a matching string and saves the index of that entry.
  103. #Searches from most recent to oldest.
  104. found_index = -1
  105. for index, line in enumerate(self.bot.mem_store['replace'][channel]):
  106. message = line
  107. msg_index = message.find(">")
  108. message = message[msg_index:]
  109. #if the current entry of mem_store contains our string, we set the index and then BREAK to stop looking
  110. if sys.version_info < (3, 0, 0):
  111. if find_msg.decode('utf-8','ignore') in message:
  112. found_index = index
  113. break
  114. else:
  115. if find_msg in message:
  116. found_index = index
  117. break
  118. #check to see if index values are positive. if not, string was not found and we're done
  119. if found_index == -1 :
  120. return None
  121. #returns the entire line
  122. submission = self.bot.mem_store['replace'][channel][found_index]
  123. return submission
  124. def handle(self, event):
  125. #first we see if we're going to try a replace or just add a line to the mem_store
  126. if event.msg.startswith(".r "):
  127. #split the msg with '.r ' stripped off beginning and divide into a search string and replace string
  128. string_token = event.msg[3:].split('|', 1)
  129. find_msg = string_token[0].rstrip()
  130. try:
  131. replace_msg = string_token[1].lstrip() #if there's nothing after the pipe, then this resolves to '' which is fine
  132. except IndexError as e:
  133. self.bot.logger.write(Logger.WARNING, line=e)
  134. return
  135. #looking for a message containing our search string
  136. newString = self.get_replacement_message(event.channel, find_msg)
  137. #because the mem_store line shows "<user> message", we have to split up the username and their message
  138. #this actually works to our advantage so we dont have to do additional calls to find out who sent what
  139. msg_index = newString.find(">")
  140. message = newString[msg_index + 2:]
  141. message = message.replace(find_msg, replace_msg)
  142. user = newString[1:msg_index]
  143. #pybot sends the new replacement message to the chat
  144. self.say(event.channel, user + " MEANT to say: " + message)
  145. # because both .r and s// are valid formats now
  146. if event.msg.startswith("s/"):
  147. self.bot.logger.write(Logger.WARNING, line="i am a log entry from within s/ in modules.replace")
  148. #alternative notation: s/<substring to replace>/<substring to replace with>
  149. string_token = event.msg[2:].split('/', 1)
  150. find_msg = string_token[0]
  151. try:
  152. replace_msg = string_token[1]
  153. except IndexError:
  154. return
  155. #looking for a message containing our search string
  156. newString = self.get_replacement_message(event.channel, find_msg)
  157. #because the mem_store line shows "<user> message", we have to split up the username and their message
  158. #this actually works to our advantage so we dont have to do additional calls to find out who sent what
  159. msg_index = newString.find(">")
  160. message = newString[msg_index + 2:]
  161. message = message.replace(find_msg, replace_msg)
  162. user = newString[1:msg_index]
  163. #pybot sends the new replacement message to the chat
  164. self.say(event.channel, user + " MEANT to say: " + message)
  165. if event.user != self.bot.NICK :
  166. self.add_buffer(event)