recap.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # -*- coding: utf-8 -*-
  2. from event import Event
  3. import random
  4. import string
  5. import time
  6. import re
  7. try:
  8. from basemodule import BaseModule
  9. except ImportError:
  10. from modules.basemodule import BaseModule
  11. class recap(BaseModule):
  12. def post_init(self):
  13. self.interests = ['__privmsg__']# should be first event in the listing.. so lines being added is a priority
  14. self.cmd = ".recap"
  15. self.help = ".recap"
  16. self.ignore_list = [self.bot.NICK, 'TSDBot', 'Bonk-Bot']
  17. self.ignore_nicks = self.create_ignore_nicks_tuple()
  18. self.RATE_LIMIT = 600 #rate limit in seconds
  19. self.MIN_WORDS = 4 #we want at least this many words for a valid line
  20. self.RECAP_LENGTH = 4 #number of lines to include in recap
  21. self.bot.mem_store['recap'] = {}
  22. for event in self.events:
  23. if event._type in self.interests:
  24. event.subscribe(self)
  25. def create_ignore_nicks_tuple(self):
  26. """creates a tuple with all nicks from self.ignore_list in <>"""
  27. nick_list = []
  28. for nick in self.ignore_list:
  29. nick_list.append("<"+nick+">")
  30. return tuple(nick_list)
  31. def get_lines(self, channel):
  32. """Given a channel, searches the qdb buffer for 4 random, suitable lines."""
  33. try:
  34. #create a copy of the channel buffer
  35. lines = list(self.bot.mem_store['qdb'][channel])
  36. #shuffle that copy up randomly
  37. random.shuffle(lines)
  38. recap = []
  39. while len(lines)>0 and len(recap) < self.RECAP_LENGTH:
  40. #as long as we have lines in the buffer and haven't chosen the desired number
  41. #keep popping lines off the top of the scrambled buffer
  42. #this ensures no duplicates ever are chosen
  43. line = lines.pop()
  44. #test for validity and add to our array of valid lines
  45. if self.valid_line(line):
  46. parts = line.split(None, 1)
  47. recap.append(self.scramble_nick(parts[0]) + " " + self.dramatize_line(parts[1]))
  48. return recap
  49. except Exception as e:
  50. self.bot.debug_print("Error getting channel buffer in get_lines")
  51. self.bot.debug_print(str(e))
  52. return False
  53. def valid_line(self, line):
  54. """Returns True if a given line matches all requirements for validity:
  55. Not an action line, longer than minimum length, not spoken by ignored nicks, no URLs"""
  56. #easy check to see if it's a line of someone speaking
  57. if line.startswith("<"):
  58. if not (line.startswith(self.ignore_nicks) or
  59. self.contains_url(line) or
  60. len(line.split()) < self.MIN_WORDS or
  61. line.split(None,1)[1].startswith((".","#"))): #check for the line after the <nick> starting with . or #
  62. return True
  63. return False
  64. def dramatize_line(self, line):
  65. """Pass a valid line in, return line with some random type of dramatic formatting"""
  66. drama = random.randint(0,750) #choose a random number
  67. line = line.strip()
  68. try:
  69. if drama in range(100,200):
  70. return line + "??"
  71. elif drama in range(200,300):
  72. return line + "..."
  73. elif drama in range(300,400):
  74. return line.upper()
  75. elif drama in range(400,500):
  76. return line + "!!"
  77. elif drama in range(500,550):
  78. return line.upper() + "!!"
  79. elif drama in range(550,600):
  80. return line.upper() + "?!?"
  81. elif drama in range(600,700):
  82. return line + " ~"
  83. elif drama in range(700,750):
  84. listline = list(line)
  85. for x in range(0, len(listline), 2):
  86. listline[x] = listline[x].upper()
  87. return ''.join(listline)
  88. elif drama == 69:
  89. return u'( ͡° ͜ʖ ͡°) (ง ͠° ͟ل͜ ͡°)ง ᕦ( ͡° ͜ʖ ͡°)ᕤ ( ͡~ ͜ʖ ͡°)'
  90. elif drama == 750:
  91. return line[::-1] #reversed string
  92. else:
  93. return line
  94. except:
  95. return line
  96. def scramble_nick(self, nick):
  97. """Given a valid nick in the format <nickname>, scramble a vowel in the nick to avoid beeping the user"""
  98. try:
  99. vowels = 'aeiou'
  100. nick_vowels = []
  101. nick_letters = list(nick[1:-1]) #grab the nick from between <> and conver to a list to make changes
  102. #create a list of tuples. each tuple is (index of a vowel in nick, the vowel at that index)
  103. for i,v in enumerate(nick_letters):
  104. if v.lower() in vowels:
  105. nick_vowels.append((i,v))
  106. #randomly choose one of the vowels in the nick to replace
  107. sel = random.choice(nick_vowels)
  108. #randomly select any vowel
  109. repl = random.choice(vowels)
  110. #keep doing the previous line until we get something different from what we're replacing
  111. while repl == sel[1].lower():
  112. repl = random.choice(vowels)
  113. #if the chosen letter to be replaced is upper case, make sure the replacement is too
  114. if nick_letters[sel[0]].isupper():
  115. nick_letters[sel[0]] = repl.upper()
  116. else:
  117. nick_letters[sel[0]] = repl
  118. #take that list of individual characters and slam it all back together into a string surrounded by <>
  119. nick = '<' + ''.join(nick_letters) + '>'
  120. #take the old nick out of the submitted line and replace it with the new scramble one
  121. return nick
  122. except IndexError:
  123. self.bot.debug_print("Error scrambling nick. Just moving on. Nick was: " + nick, error=True)
  124. return nick #if there's any problems at all, just don't scramble the nick. odd cases like no vowels
  125. def contains_url(self, line):
  126. """Given a string, returns True if there is a url present"""
  127. urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
  128. if urls:
  129. return True
  130. return False
  131. def check_rate(self, channel):
  132. """Check to see if the given channel has allowed enough time to pass before calling recap again. Return True
  133. and set the new time limit if true. Return False if not."""
  134. try:
  135. if self.get_timediff(channel) <= 0:
  136. self.bot.mem_store['recap'][channel] = int(time.time()) + self.RATE_LIMIT
  137. return True
  138. else:
  139. return False
  140. except KeyError:
  141. self.bot.mem_store['recap'][channel] = int(time.time()) + self.RATE_LIMIT
  142. return True
  143. def reset_timer(self, channel):
  144. """If there's an error getting a recap, call this to reset lockdown timer"""
  145. self.bot.mem_store['recap'][channel] = int(time.time())
  146. def get_timediff(self, channel):
  147. """Return how much time remains in the function lockdown"""
  148. return self.bot.mem_store['recap'][channel] - int(time.time())
  149. def get_episode(self):
  150. """Return a list with two elements: a random show title and episode name"""
  151. titles = ["Internet Relay Chat",
  152. "Multiplayer Notepad",
  153. "Wise Use of Work Hours",
  154. "The Kanbo Korner",
  155. "2 Grils?",
  156. "TSDIRC: The Rise and Fall of a Legend",
  157. "Exodus",
  158. "Tex's Tricks",
  159. "Top Fun",
  160. "Big Anime Robots",
  161. "The Meme Machine",
  162. "The Botpocalypse"
  163. ]
  164. episodes = ["The Mystery of DeeJ",
  165. "Paddy's Big Goodbye",
  166. "Hickory Dickory...Dead",
  167. "BoneKin Dies",
  168. "Dr. DeeJ and Mr. DorJ",
  169. "The Double Dorj",
  170. "Bonk-Bot's Crash Test Dummies",
  171. "IRC Finds a Dead Body",
  172. "Beach Party",
  173. "Everyone Gets Sucked Back in Time",
  174. "Brass Tax",
  175. "Return to Bonk Mountain",
  176. "The Incredible Bonk",
  177. "Paddy Gets Big",
  178. "Dawn of the New Age",
  179. "Tex Goes to Work",
  180. "Planet of the IRC",
  181. "Nart Gets His GED",
  182. "High School Drama",
  183. "TD Moves Out",
  184. "TDSpiral Paints a Picture",
  185. "Kapowaz Wins",
  186. "Banana Gets High",
  187. "Dragon's Laird",
  188. "StarLaird",
  189. "Kanboface",
  190. "Eternity, Loyalty, Honesty",
  191. "Paddy on Parole",
  192. "The HBO Beauty Contest, Pt. 2",
  193. "Snipe Reviews Halo 5",
  194. "1-800-GET-GOOD",
  195. "A Baby Wheel",
  196. "BoneKin Ruins the Creative Process",
  197. "The 80 Proof Spoof",
  198. "IRC Forgets to Set Their Holiday Nicks",
  199. "Hot Diggety Dorj",
  200. "Yapok Talks",
  201. "Hellmitre Argues With His Bot",
  202. "Pybot Strikes Back",
  203. "Where's Schooly?",
  204. "Heavy Is the BanHammer",
  205. "Sunbreaker or Sunbroken?",
  206. "Testing in Production",
  207. "BoneKin Codes a New Module and Forgets How to Use Git",
  208. "The Curse of the Spiduh",
  209. "Snipe Quits Halo (Part 2)",
  210. "Monopoly Is A Fun Game",
  211. "Dr. GV, PhD, although I guess if he was a medical doctor he wouldn't have a PhD? Or maybe they can, I don't know. I know he'd be called 'Dr.' though. I think they should make that clearer, like in the dictionary or wherever they spell things out like that. But I guess it wouldn't be an English thing it'd be a medical licensing and terminology thing? Uuuuuuugggggghhhh it's already so late and I was supposed to go to bed 23 minutes ago but then t"
  212. ]
  213. return [random.choice(titles), random.choice(episodes)]
  214. def handle(self, event):
  215. if event.msg == ".recap":
  216. #check the rate first, then continue with processing
  217. if self.check_rate(event.channel):
  218. episode = self.get_episode()
  219. recap = self.get_lines(event.channel)
  220. if not recap:
  221. self.reset_timer(event.channel)
  222. self.say(event.channel, "Error processing recap request")
  223. return
  224. self.say(event.channel, "Previously on \"" + episode[0] + "\": ")
  225. for r in recap:
  226. self.say(event.channel, r)
  227. self.say(event.channel, "Tonight's episode: \"" + episode[1] + "\"")
  228. else:
  229. timediff = str(self.get_timediff(event.channel))
  230. self.say(event.user, "Recap is on lockdown for " + timediff + " more seconds.")