PageRenderTime 29ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/bot/__init__.py

https://github.com/vinces1979/Plank
Python | 291 lines | 274 code | 16 blank | 1 comment | 14 complexity | 91402dc428ef26421c4337689fdb622c MD5 | raw file
  1. """Data and message handling"""
  2. import re
  3. import cPickle as pickle
  4. from random import choice
  5. from lxml import html
  6. import redis
  7. from twisted.internet import reactor, protocol
  8. from twisted.web import client
  9. from twisted.web.client import HTTPClientFactory, _parse
  10. from bot.client import PlankIRCProtocol
  11. RDB = redis.Redis()
  12. Nick_point_re = re.compile(r"([^\s]+?)([,:\s]*[+-]{2})")
  13. URL_re = re.compile("http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+")
  14. BAD_WORDS = ["lol", "lmfao"]
  15. DEGRADED = re.compile( "|".join(r"\b%s\b" % w for w in BAD_WORDS) )
  16. def cleanNick(nick):
  17. nick = nick.strip("@")
  18. if nick.lower().endswith("afk"):
  19. nick = nick[:-3]
  20. return nick
  21. def getPage(url, contextFactory=None, *args, **kwargs):
  22. scheme, host, port, path = _parse(url)
  23. factory = HTTPClientFactory(url, *args, **kwargs)
  24. reactor.connectTCP(host, port, factory)
  25. return factory
  26. PORNWORDS = ['fuck',
  27. 'pussy',
  28. 'tits',
  29. 'anal',
  30. 'porn',
  31. 'xxx',
  32. 'viagra',
  33. 'sex',
  34. 'cams',
  35. 'blowjob',
  36. 'handjob',
  37. 'threesome',
  38. 'dildo',
  39. 'cock',
  40. 'cum',
  41. 'lemonparty',
  42. 'nudity',
  43. '100% free',
  44. 'adult video',
  45. '18+',
  46. 'adult oriented',
  47. 'completely free',
  48. 'cute girls',
  49. 'nsfw',
  50. 'butt',
  51. 'boobs',
  52. 'shemale',
  53. 'dick',
  54. 'ass',
  55. ]
  56. PORN_re = re.compile("|".join(r"\b%s\b" % w for w in PORNWORDS), re.I)
  57. class IRCProtocol(PlankIRCProtocol):
  58. nickname = 'plank'
  59. nicklist_key = "plank:%s:nicks"
  60. def handle_nicklist(self, nicklist, channel):
  61. print "storing>>", nicklist, channel
  62. key = self.nicklist_key % channel
  63. for nick in nicklist:
  64. if nick != self.nickname:
  65. RDB.sadd(key, cleanNick(nick))
  66. def handle_nick_join(self, nick, channel):
  67. nick = cleanNick(nick)
  68. RDB.sadd(self.nicklist_key % channel, nick)
  69. count = RDB.hincrby("plank:%s" % channel, nick, 1)
  70. self.me(channel, "gives %s a point for coming out to play" % (nick))
  71. def handle_kick(self, op, channel, nick, msg):
  72. count = RDB.hincrby("plank:%s" % channel, cleanNick(op), -1)
  73. self.me(channel, "takes a point from %s, and tells them to go sit in the corner. " % op)
  74. def handle_nick_change(self, oldnick, newnick):
  75. nick = cleanNick(newnick)
  76. for channel in self.factory.channels:
  77. RDB.sadd(self.nicklist_key % channel, nick)
  78. def afterSignOn(self):
  79. global BAD_WORDS, DEGRADED
  80. if self.factory.badwords:
  81. BAD_WORDS = self.factory.badwords
  82. DEGRADED = re.compile( "|".join(r"\b%s\b" % w for w in BAD_WORDS) )
  83. def url_callback(self, data, factory, url, nick, channel):
  84. url_type = "something awesome"
  85. incr = 1
  86. ct = factory.response_headers['content-type']
  87. parse_html = False
  88. extra = ""
  89. if ct:
  90. if "image" in ct[0]:
  91. incr += 1
  92. url_type = "an image with the class"
  93. elif "html" in ct[0]:
  94. parse_html = True
  95. url_type = "a web page"
  96. if "reddit.com" in url:
  97. incr += 1
  98. url_type = "some reddit words of wisdom"
  99. prob = 1
  100. if parse_html:
  101. words = PORN_re.findall(html.fromstring(data).text_content())
  102. total = len(words)
  103. if total <= 2:
  104. prob = 1
  105. elif 2 < total < 5:
  106. prob = 10
  107. elif 5 >= total < 15:
  108. prob = 40
  109. elif total > 15:
  110. prob = 90
  111. RDB.hset("plank:urls", url,
  112. pickle.dumps({'prob': prob, 'url_type':url_type}))
  113. self.url_points(nick, channel, incr, prob, url_type)
  114. def url_points(self, nick, channel, incr, prob, url_type):
  115. if prob == 10:
  116. incr += 1
  117. if prob == 40:
  118. incr += 2
  119. if prob == 90:
  120. incr += 4
  121. extra = ""
  122. if url_type == "a web page":
  123. extra = "[ %s%% chance of porn ]" % prob
  124. count = RDB.hincrby("plank:%s" % channel, nick, incr)
  125. self.me(channel, "gives %s %s point%s for sharing %s %s" % (nick, incr, "s" if incr > 1 else "", url_type, extra))
  126. def handle_message(self, user, channel, nick, host, message):
  127. print "handle message", user, channel, nick, host, message
  128. incr = None
  129. check = Nick_point_re.search(message)
  130. author = cleanNick(nick)
  131. if check:
  132. incr = 1 if message.endswith("++") else -1
  133. nick = cleanNick(check.groups()[0])
  134. if nick == author:
  135. return False
  136. if nick.rstrip("_") == self.nickname:
  137. if incr < 0:
  138. count = RDB.hincrby("plank:%s" % channel, author, -1)
  139. self.me(channel, "takes a point from %s for trying to take points from a bot! *shakes head*" % author)
  140. else:
  141. reasons = [
  142. "I once punched Chuck Norris!",
  143. "I was created to be better then you.",
  144. " .... 42",
  145. "your ugly",
  146. "I had a threesome with your mom and grandma",
  147. "your mom liked it too much last night",
  148. "of rule 34",
  149. "I am a robot and your not, so play safe",
  150. "you like to look at granny porn",
  151. "your argument is invalid",
  152. "you like goats",
  153. ]
  154. self.msg(channel, "I don't accept points from you because %s" % choice(reasons))
  155. return
  156. channel_nicks = RDB.smembers(self.nicklist_key % channel)
  157. if nick not in channel_nicks:
  158. print "unknown nick"
  159. return
  160. count = RDB.hincrby("plank:%s" % channel, nick, incr)
  161. self.msg(channel, "%s your ranking is now %s" % (nick, count))
  162. elif URL_re.search(message):
  163. url = URL_re.search(message).group()
  164. prob = RDB.hget("plank:urls", url)
  165. if not prob:
  166. d = getPage(url)
  167. d.deferred.addCallback(self.url_callback, d, url, nick, channel)
  168. else:
  169. url = pickle.loads(prob)
  170. self.url_points(nick, channel, 1, url['prob'], url['url_type'])
  171. elif DEGRADED.search(message):
  172. words = DEGRADED.findall(message)
  173. total = len(words) * -1
  174. count = RDB.hincrby("plank:%s" % channel, nick, total)
  175. self.msg(channel, "%s lost %s point%s for saying %s" % (nick, total , "s" if total > 1 else "", ", ".join(words) ))
  176. def command_help(self, nick, channel, rest):
  177. msgs = [
  178. "nick++ : give a point to nick",
  179. "nick-- : remove a point from a nick",
  180. "!badwords: lose a point when you say these words",
  181. "!myrank: find out your ranking",
  182. "!rank nick: show the rank for this nick",
  183. "!ranks: show all ranking for this channel",
  184. "!ranklist: show a list of ranks for the channel ",
  185. "!joke: grab random joke from store",
  186. "!joketotal: return the number of jokes stored",
  187. "!players: list the players with points",
  188. "Extra points:",
  189. " Get points for urls posted",
  190. ]
  191. return msgs
  192. def command_badwords(self, nick, channel, rest):
  193. return ", ".join(BAD_WORDS)
  194. def command_joketotal(self, nick, channel, rest):
  195. return RDB.scard("jokes")
  196. def command_joke(self, nick, channel, rest):
  197. joke = RDB.srandmember("jokes")
  198. if joke:
  199. return joke
  200. else:
  201. return "Sorry, I am boring and don't know any good jokes"
  202. def command_addjoke(self, nick, channel, rest):
  203. if RDB.sadd("jokes", rest):
  204. return "added"
  205. return "error"
  206. def command_deljoke(self, nick, channel, rest):
  207. if RDB.srem("jokes", rest):
  208. return "gone"
  209. return "error"
  210. def command_myrank(self, nick, channel, rest):
  211. if rest:
  212. rest = rest.split(" ")[0].strip()
  213. count = RDB.hget("plank:%s" % (rest or channel), nick)
  214. msg = "your ranking is %s" % (count or 0)
  215. if rest:
  216. msg += " in %s" % rest
  217. return msg
  218. def command_rank(self, nick, channel, rest):
  219. other_nick = rest.rstrip(":")
  220. if not other_nick:
  221. other_nick = nick
  222. data = RDB.hget("plank:%s" % channel, other_nick)
  223. if not data:
  224. return "unknown nick %s" % other_nick
  225. else:
  226. return "Rank: %s %s" % (other_nick if other_nick != nick else "", data)
  227. def command_ranks(self, nick, channel, rest):
  228. if rest:
  229. channel = rest.split(" ")[0].strip()
  230. data = RDB.hgetall("plank:%s" % channel)
  231. if "_nicks" in data:
  232. data.pop("_nicks")
  233. print data
  234. data = sorted(data.items(), key=lambda nick: int(nick[1]) , reverse=1)
  235. msgs = []
  236. for nick, value in data:
  237. msgs.append("%s: %s" % (nick, value))
  238. return msgs
  239. def command_ranklist(self, nick, channel, rest):
  240. msgs = self.command_ranks(nick, channel, rest)
  241. return "; ".join(msgs)
  242. def command_nicklist(self, nick, channel, rest):
  243. key = "plank:%s:nicks" % channel
  244. return ", ".join(RDB.smembers(key))
  245. def command_players(self, nick, channel, rest):
  246. data = RDB.hgetall("plank:%s" % channel)
  247. return ", ".join(key for key,value in data.items())
  248. class Factory(protocol.ReconnectingClientFactory):
  249. protocol = IRCProtocol
  250. channels = []
  251. def __init__(self, trigger="!", channels=None, bad=None):
  252. self.channels = channels or []
  253. self.trigger = trigger
  254. self.badwords = bad or []