def urbandictionary_lookup(self, channel, nick, msg, args): query = args['query'] try: r = requests.get(API_URL % query) r.raise_for_status() except requests.exceptions.HTTPError: self.bot.msg(channel, 'No results for %s' % query) return False data = r.json() if 'result_type' in data: if data['result_type'] == "no_results": self.bot.msg('No results for %s' % query) return False else: definition = data['list'][0] word = definition['word'].encode('UTF-8') def_text = definition['definition'].encode('UTF-8') permalink = definition['permalink'].encode('UTF-8') formatted_msg = assembleFormattedText(A.bold[word]) formatted_msg += assembleFormattedText(A.normal[": "]) formatted_msg += def_text.split('\r\n')[0] formatted_msg += assembleFormattedText(A.bold[" See more: "]) formatted_msg += assembleFormattedText(A.normal[permalink]) self.bot.msg(channel, formatted_msg) else: self.bot.msg(channel, 'No results for %s' % query) return False
def execute(self, message): """ @type message: IRCMessage """ subString = self._mangleEscapes(message.Parameters) try: segments = list(self._parseSubcommandTree(subString)) except UnbalancedBracesException as e: red = assembleFormattedText(A.bold[A.fg.lightRed['']]) normal = assembleFormattedText(A.normal['']) error = subString[:e.column] + red + subString[e.column] + normal + subString[e.column+1:] error = self._unmangleEscapes(error, False) return [IRCResponse(ResponseType.Say, u"Sub Error: {}".format(e.message), message.ReplyTo), IRCResponse(ResponseType.Say, error, message.ReplyTo)] prevLevel = -1 responseStack = [] extraVars = {} metadata = {} for segment in segments: (level, command, start, end) = segment # We've finished executing subcommands at the previous depth, # so replace subcommands with their output at the current depth if level < prevLevel: command = self._substituteResponses(command, responseStack, level, extraVars, start) # Replace any extraVars in the command for var, value in iteritems(extraVars): command = re.sub(r'\$\b{}\b'.format(re.escape(var)), u'{}'.format(value), command) # Build a new message out of this segment inputMessage = IRCMessage(message.Type, message.User.String, message.Channel, self.bot.commandChar + command.lstrip(), self.bot, metadata=metadata) # Execute the constructed message if inputMessage.Command.lower() in self.bot.moduleHandler.mappedTriggers: response = self.bot.moduleHandler.mappedTriggers[inputMessage.Command.lower()].execute(inputMessage) """@type : IRCResponse""" else: return IRCResponse(ResponseType.Say, u"'{}' is not a recognized command trigger".format(inputMessage.Command), message.ReplyTo) # Push the response onto the stack responseStack.append((level, response.Response, start, end)) # Update the extraVars dict extraVars.update(response.ExtraVars) metadata = self._recursiveMerge(metadata, response.Metadata) prevLevel = level responseString = self._substituteResponses(subString, responseStack, -1, extraVars, -1) responseString = self._unmangleEscapes(responseString) return IRCResponse(ResponseType.Say, responseString, message.ReplyTo, extraVars=extraVars, metadata=metadata)
def _guess(self, message): """ @type message: IRCMessage @rtype: IRCResponse """ channel = message.ReplyTo.lower() if channel not in self.gameStates: return IRCResponse(ResponseType.Say, u'[Hangman] no game running, use {}hangman start to begin!'.format(self.bot.commandChar), message.ReplyTo) responses = [] gs = self.gameStates[channel] guess = message.Parameters.lower() # single letter if len(guess) == 1: try: correct = gs.guessLetter(guess) except (AlreadyGuessedException, InvalidCharacterException) as e: return self._exceptionFormatter(e, message.ReplyTo) # whole phrase else: try: correct = gs.guessPhrase(guess) except (WrongPhraseLengthException, PhraseMismatchesGuessesException, PhraseUsesKnownBadLettersException) as e: return self._exceptionFormatter(e, message.ReplyTo) user = message.User.Name # split the username with a zero-width space # hopefully this kills client highlighting on nick mentions #user = user[:1] + u'\u200b' + user[1:] # try a tiny arrow instead, some clients actually render zero-width spaces colUser = user[:1] + u'\u034e' + user[1:] if correct: colUser = assembleFormattedText(A.normal[A.fg.green[colUser]]) else: colUser = assembleFormattedText(A.normal[A.fg.red[colUser]]) responses.append(IRCResponse(ResponseType.Say, u'{} - {}'.format(gs.render(), colUser), message.ReplyTo)) if gs.finished: if correct: responses.append(IRCResponse(ResponseType.Say, u'[Hangman] Congratulations {}!'.format(user), message.ReplyTo)) else: responses.append(IRCResponse(ResponseType.Say, u'[Hangman] {} blew up the bomb! The {} was {}'.format(user, gs.wOrP(), gs.phrase), message.ReplyTo)) self._stop(message, suppressMessage=True) return responses
def _renderGuesses(self): colouredGuesses = [] for g in self.guesses: if g in self.phrase: colouredGuesses.append(assembleFormattedText(A.bold[A.fg.green[g]])) else: colouredGuesses.append(assembleFormattedText(A.fg.red[g])) reset = assembleFormattedText(A.normal['']) return u'[{}{}]'.format(u''.join(colouredGuesses), reset)
def whoresponse(self, channel): who_users = communicator.handleWho(self, channel) if len(who_users) > 0: return [irc.assembleFormattedText( irc.attributes.bold[irc.attributes.underline["List of relayed users on ", channel]]), " ".join(who_users), irc.assembleFormattedText(irc.attributes.bold[irc.attributes.underline[ "END List of relayed users on ", channel]])] return None
def FollowKickstarter(self, ksID, message): webPage = WebUtils.fetchURL('https://www.kickstarter.com/projects/{0}/'.format(ksID)) soup = BeautifulSoup(webPage.body) data = [] title = soup.find(class_='title') if title is not None: creator = soup.find(id='name') if creator is not None: data.append(assembleFormattedText(A.normal['{0}', A.fg.gray[' by '], '{1}']).format(title.h2.text.strip(), creator.text.strip())) else: data.append(title.h2.text.strip()) stats = soup.find(id='stats') backerCount = stats.find(id='backers_count') if backerCount is not None: data.append('Backers: {0:,}'.format(int(backerCount['data-backers-count']))) pledged = stats.find(id='pledged') if pledged is not None: if float(pledged['data-percent-raised']) >= 1.0: percentageString = A.fg.green['({3:,.0f}% funded)'] else: percentageString = A.fg.red['({3:,.0f}% funded)'] pledgedString = assembleFormattedText(A.normal['Pledged: {0:,.0f}', A.fg.gray['/'], '{1:,.0f} {2} ', percentageString]) data.append(pledgedString.format(float(pledged['data-pledged']), float(pledged['data-goal']), pledged.data['data-currency'], float(pledged['data-percent-raised']) * 100)) findState = soup.find(id='main_content') if 'Project-state-canceled' in findState['class']: data.append(assembleFormattedText(A.normal[A.fg.red['Cancelled']])) elif 'Project-state-failed' in findState['class']: data.append(assembleFormattedText(A.normal[A.fg.red['Failed']])) elif 'Project-state-successful' in findState['class']: data.append(assembleFormattedText(A.normal[A.fg.green['Successful']])) elif 'Project-state-live' in findState['class']: duration = stats.find(id='project_duration_data') if duration is not None: remaining = float(duration['data-hours-remaining']) days = math.floor(remaining/24) hours = remaining/24 - days data.append('Duration: {0:.0f} days {1:.1f} hours to go'.format(days, hours)) return IRCResponse(ResponseType.Say, self.graySplitter.join(data), message.ReplyTo)
def stock(bot, args, sender, source): try: resp = yql(args[0]) except: bot.reply(source, sender, "Couldn't get stock data: {}".format(sys.exc_info()[0])) raise if resp["count"] == 0: bot.reply( source, sender, assembleFormattedText(A.normal["Couldn't find the ticker symbol ", A.bold[args[0]]])) return quote = resp["results"]["quote"] if quote["LastTradePriceOnly"] is None: bot.reply( source, sender, assembleFormattedText(A.normal["Couldn't find the ticker symbol ", A.bold[args[0]]])) return change = float(quote["Change"]) price = float(quote["LastTradePriceOnly"]) name = quote["Name"] if price == 0 and change == 0: # Company is dead bot.reply( source, sender, assembleFormattedText(A.normal[A.bold[name], " is no longer trading."])) return color = A.fg.gray percent = (change / (price - change)) * 100 if change > 0: color = A.fg.green change = "+{}".format(change) elif change < 0: color = A.fg.lightRed bot.reply( source, sender, assembleFormattedText( A.normal[A.bold[name], " (", A.bold[quote["Symbol"]], "): ", str(price), " ", color["{} ({:.2f}%)".format(change, percent)]]))
def FollowTwitch(self, channel, message): # Heavily based on Didero's DideRobot code for the same # https://github.com/Didero/DideRobot/blob/06629fc3c8bddf8f729ce2d27742ff999dfdd1f6/commands/urlTitleFinder.py#L37 # TODO: other stats? if self.twitchClientID is None: return IRCResponse(ResponseType.Say, '[Twitch Client ID not found]', message.ReplyTo) chanData = {} channelOnline = False twitchHeaders = [('Accept', 'application/vnd.twitchtv.v3+json'), ('Client-ID', self.twitchClientID)] webPage = WebUtils.fetchURL( u'https://api.twitch.tv/kraken/streams/{}'.format(channel), twitchHeaders) streamData = json.loads(webPage.body) if 'stream' in streamData and streamData['stream'] is not None: chanData = streamData['stream']['channel'] channelOnline = True elif 'error' not in streamData: webPage = WebUtils.fetchURL( u'https://api.twitch.tv/kraken/channels/{}'.format(channel), twitchHeaders) chanData = json.loads(webPage.body) if len(chanData) > 0: if channelOnline: channelInfo = assembleFormattedText( A.fg.green['']) + u'{}'.format( chanData['display_name']) + assembleFormattedText( A.normal['']) else: channelInfo = assembleFormattedText( A.fg.red['']) + u'{}'.format( chanData['display_name']) + assembleFormattedText( A.normal['']) channelInfo += u' "{}"'.format( re.sub(r'[\r\n]+', self.graySplitter, chanData['status'].strip())) if chanData['game'] is not None: channelInfo += assembleFormattedText( A.normal[A.fg.gray[', playing '], u'{}'.format(chanData['game'])]) if chanData['mature']: channelInfo += assembleFormattedText( A.normal[A.fg.lightRed[' [Mature]']]) if channelOnline: channelInfo += assembleFormattedText( A.normal[A.fg.green[' (Live with {0:,d} viewers)'.format( streamData['stream']['viewers'])]]) else: channelInfo += assembleFormattedText( A.normal[A.fg.red[' (Offline)']]) return IRCResponse( ResponseType.Say, channelInfo, message.ReplyTo, {'urlfollowURL': 'https://twitch.tv/{}'.format(channel)})
def stock(bot, args, sender, source): try: resp = yql(args[0]) except: bot.reply(source, sender, "Couldn't get stock data: {}".format(sys.exc_info()[0])) raise if resp["count"] == 0: bot.reply(source, sender, assembleFormattedText( A.normal["Couldn't find the ticker symbol ", A.bold[args[0]]] )) return quote = resp["results"]["quote"] if quote["LastTradePriceOnly"] is None: bot.reply(source, sender, assembleFormattedText( A.normal["Couldn't find the ticker symbol ", A.bold[args[0]]] )) return change = float(quote["Change"]) price = float(quote["LastTradePriceOnly"]) name = quote["Name"] if price == 0 and change == 0: # Company is dead bot.reply(source, sender, assembleFormattedText( A.normal[A.bold[name], " is no longer trading."] )) return color = A.fg.gray percent = (change / (price - change)) * 100 if change > 0: color = A.fg.green change = "+{}".format(change) elif change < 0: color = A.fg.lightRed bot.reply(source, sender, assembleFormattedText( A.normal[ A.bold[name], " (", A.bold[quote["Symbol"]], "): ", str(price), " ", color["{} ({:.2f}%)".format(change, percent)] ] ))
def wolfram(bot, args, sender, source): query = args[0].strip() key = "wolfram:{}".format(query.lower()) if dave.config.redis.exists(key): bot.reply(source, sender, dave.config.redis.get(key).decode('utf-8')) dave.config.redis.setex(key, 60, dave.config.redis.get(key)) return if query: client = wolframalpha.Client(dave.config.config["api_keys"]["wolfram"]) res = client.query(query) pods = list(res.pods) if len(pods) > 0: resultpod = next(res.results) result = resultpod.text if "image" in pods[0].text: result = resultpod.img if len(result) > 500: result = result[:497] + "..." res = assembleFormattedText(A.normal[A.bold[pods[0].text], ": {}".format(result)]) dave.config.redis.setex(key, 60, res) bot.reply(source, sender, res)
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) != 1: return IRCResponse(ResponseType.Say, self.help, message.ReplyTo) subreddit = message.ParameterList[0].lower() url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}" url = url.format(subreddit, random.randint(0, 100)) response = WebUtils.fetchURL(url, self.headers) jsonResponse = json.loads(response.body) images = jsonResponse['data'] if not images: return IRCResponse(ResponseType.Say, "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)" .format(subreddit), message.ReplyTo) image = random.choice(images) data = [] if image['title'] is not None: data.append(image['title']) if image['nsfw']: data.append(u'\x034\x02NSFW!\x0F') if image['animated']: data.append(u'\x032\x02Animated!\x0F') data.append(image['link']) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
def FollowTwitter(self, tweeter, tweetID, message): webPage = WebUtils.fetchURL('https://twitter.com/{0}/status/{1}'.format(tweeter, tweetID)) soup = BeautifulSoup(webPage.body) tweet = soup.find(class_='permalink-tweet') user = tweet.find(class_='username').text tweetText = tweet.find(class_='tweet-text') tweetTimeText = tweet.find(class_='client-and-actions').text.strip() try: tweetTimeText = time.strftime('%Y/%m/%d %H:%M', time.strptime(tweetTimeText, '%I:%M %p - %d %b %Y')) except ValueError: pass links = tweetText.find_all('a', {'data-expanded-url': True}) for link in links: link.string = ' ' + link['data-expanded-url'] embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'}) for link in embeddedLinks: link.string = ' ' + link['href'] text = StringUtils.unescapeXHTML(tweetText.text) text = re.sub('[\r\n]+', self.graySplitter, text) formatString = unicode(assembleFormattedText(A.normal[A.fg.gray['[{0}]'], A.bold[' {1}:'], ' {2}'])) return IRCResponse(ResponseType.Say, formatString.format(tweetTimeText, user, text), message.ReplyTo, {'urlfollowURL': 'https://twitter.com/{}/status/{}'.format(tweeter, tweetID)})
def FollowTwitter(self, tweeter, tweetID, message): webPage = WebUtils.fetchURL('https://twitter.com/{0}/status/{1}'.format(tweeter, tweetID)) soup = BeautifulSoup(webPage.body) tweet = soup.find(class_='permalink-tweet') user = tweet.find(class_='username').text tweetText = tweet.find(class_='tweet-text') tweetTimeText = tweet.find(class_='client-and-actions').text.strip() try: tweetTimeText = time.strftime('%Y/%m/%d %H:%M', time.strptime(tweetTimeText, '%I:%M %p - %d %b %Y')) except ValueError: pass links = tweetText.find_all('a', {'data-expanded-url': True}) for link in links: link.string = link['data-expanded-url'] embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'}) for link in embeddedLinks: link.string = link['href'] text = StringUtils.unescapeXHTML(tweetText.text) text = re.sub('[\r\n]+', self.graySplitter, text) formatString = unicode(assembleFormattedText(A.normal[A.fg.gray['[{0}]'], A.bold[' {1}:'], ' {2}'])) return IRCResponse(ResponseType.Say, formatString.format(tweetTimeText, user, text), message.ReplyTo, {'urlfollowURL': 'https://twitter.com/{}/status/{}'.format(tweeter, tweetID)})
def FollowTwitter(self, tweeter, tweetID, message): webPage = WebUtils.fetchURL('https://twitter.com/{0}/status/{1}'.format(tweeter, tweetID)) soup = BeautifulSoup(webPage.body) tweet = soup.find(class_='permalink-tweet') user = tweet.find(class_='username').text tweetText = tweet.find(class_='tweet-text') links = tweetText.find_all('a', {'data-expanded-url': True}) for link in links: link.string = link['data-expanded-url'] embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'}) for link in embeddedLinks: link.string = link['href'] text = StringUtils.unescapeXHTML(tweetText.text) text = re.sub('[\r\n]+', self.graySplitter, text) formatString = unicode(assembleFormattedText(A.normal[A.bold['{0}:'], ' {1}'])) return IRCResponse(ResponseType.Say, formatString.format(user, text), message.ReplyTo)
def _renderGuesses(self): colouredGuesses = [] for g in self.guesses: if g in self.phrase: g = g.encode('utf-8') colouredGuesses.append( assembleFormattedText(A.bold[A.fg.green[g]])) else: g = g.encode('utf-8') colouredGuesses.append(assembleFormattedText(A.fg.red[g])) reset = assembleFormattedText(A.normal['']) colouredGuesses = [ c.decode(encoding='utf-8', errors='ignore') for c in colouredGuesses ] return u'[{}{}]'.format(u''.join(colouredGuesses), reset)
def privmsg(self, user, channel, msg, color=None): """This will get called when the bot receives a message.""" user = user.split('!', 1)[0] # Check to see if they're sending me a private message if channel == self.nickname: print("[*] info: received a private message: " + msg) msg = "Wat zit je nu weer te ratelen. Gooi het in de groep!" self.msg(user, irc.assembleFormattedText(irc.attributes.normal[irc.attributes.fg.magenta[msg]])) # Otherwise check to see if it is a message directed at me elif msg.startswith(settings.control_char) or msg.endswith(settings.nickname): if user not in settings.banned: # look for control_char or nick and remove it from the message if msg[0] == settings.control_char: msg = msg[1:] elif msg[-5:] == " " + settings.nickname: msg = msg[:-5] command = msg.split()[0] # filter command from message try: arg = msg.split(' ', 1)[1] # the stuff after the command except IndexError: arg = '' response = controllers.message.handle_message(command, arg, user) if response is not None: self.msg(channel, response)
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) < 3: return IRCResponse(ResponseType.Say, self.help, message.ReplyTo) try: amount = float(message.ParameterList[0]) offset = 1 except ValueError: amount = 1.0 offset = 0 ccFrom = message.ParameterList[offset] ccTo = message.ParameterList[offset+2:] ccTo = ",".join(ccTo) url = "https://api.fixer.io/latest?base={}&symbols={}" url = url.format(ccFrom, ccTo) response = WebUtils.fetchURL(url) jsonResponse = json.loads(response.body) rates = jsonResponse['rates'] if not rates: return IRCResponse(ResponseType.Say, "Some or all of those currencies weren't recognized!", message.ReplyTo) data = [] for curr,rate in rates.iteritems(): data.append("{} {}".format(rate*amount, curr)) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
def _stringifyTweet(self, tweet): """ turn a tweet object into a nice string for us to send to IRC @type tweet: dict[str, T/str] """ retweet = None # get the original tweet if this is a retweet if 'retweeted_status' in tweet: retweet = tweet tweet = retweet['retweeted_status'] tweetText = StringUtils.unescapeXHTML(tweet['text']) tweetText = re.sub('[\r\n]+', StringUtils.graySplitter, tweetText) for url in tweet['entities']['urls']: tweetText = tweetText.replace(url['url'], url['expanded_url']) timestamp = parser.parse(tweet['created_at']) timeString = timestamp.strftime('%A, %Y-%m-%d %H:%M:%S %Z') delta = datetime.datetime.utcnow() - timestamp.replace(tzinfo=None) deltaString = StringUtils.deltaTimeToString(delta) tweetText = u'{} (tweeted {}, {} ago)'.format(tweetText, timeString, deltaString) if retweet is None: user = tweet['user']['screen_name'] else: user = retweet['user']['screen_name'] tweetText = 'RT {}: {}'.format(tweet['user']['screen_name'], tweetText) link = u'https://twitter.com/{}/status/{}'.format(tweet['user']['screen_name'], tweet['id_str']) link = WebUtils.shortenGoogl(link) formatString = unicode(assembleFormattedText(A.normal[A.bold['@{0}>'], ' {1} {2}'])) newTweet = formatString.format(user, tweetText, link) return newTweet
def applyColorFormat(self, *msg, **kwargs): """put some nice colors on the message""" colors = kwargs.get('colors') toAssemble = [] log.debug(msg) log.debug(colors) msg = [m.encode('utf-8') for m in msg] if not colors or len(colors) != len(msg): log.debug("no colors") for m in msg: log.debug(m) log.debug(type(m)) toAssemble.append(irc.attributes.fg.gray[m]) else: log.debug("colors!") for m, c in zip(msg, colors): log.debug(m) log.debug(c) if not c: log.debug("no c") toAssemble.append(irc.attributes.fg.gray[m]) else: log.debug("using special color") log.debug(c) toAssemble.append(c[m]) return irc.assembleFormattedText(irc.attributes.normal[toAssemble])
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0 or len(message.ParameterList) > 2: return IRCResponse(ResponseType.Say, self.help(None), message.ReplyTo) if not self.imgurClientID: return IRCResponse(ResponseType.Say, u'[imgur client ID not found]', message.ReplyTo) subreddit = message.ParameterList[0].lower() if len(message.ParameterList) == 2: try: if len(message.ParameterList[1]) < 20: topRange = int(message.ParameterList[1]) else: raise ValueError if topRange < 0: raise ValueError except ValueError: return IRCResponse(ResponseType.Say, "The range should be a positive integer!", message.ReplyTo) else: topRange = 100 url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}" url = url.format(subreddit, random.randint(0, topRange)) try: response = self.bot.moduleHandler.runActionUntilValue('fetch-url', url, extraHeaders=self.headers) jsonResponse = json.loads(response.body) except json.JSONDecodeError: return IRCResponse(ResponseType.Say, "[The imgur API doesn't appear to be responding correctly]", message.ReplyTo) images = jsonResponse['data'] if not images: return IRCResponse(ResponseType.Say, "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)" .format(subreddit), message.ReplyTo) image = random.choice(images) data = [] if 'title' in image and image['title'] is not None: data.append(image['title']) if 'nsfw' in image and image['nsfw']: data.append(u'\x034\x02NSFW!\x0F') if 'animated' in image and image['animated']: data.append(u'\x032\x02Animated!\x0F') if 'gifv' in image: data.append(image['gifv']) else: data.append(image['link']) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
def pollen(bot, args, sender, source): postcode = args[0].lower() text = None if not dave.config.redis.exists("pollen:{}".format(postcode)): res = get("https://www.bbc.co.uk/weather/{}".format(postcode)) soup = BeautifulSoup(res.text, "html.parser") element = soup.find_all("div", class_="environmental-index pollen-index") if element: pollen = element[0].find("span") if pollen: text = pollen.text dave.config.redis.setex("pollen:{}".format(postcode), 1800, text) else: text = dave.config.redis.get("pollen:{}".format(postcode)) if text: bot.reply( source, sender, assembleFormattedText(A.normal["The pollen count is currently ", A.bold[str(text)], " in ", postcode.upper()]))
def FollowTwitter(self, tweeter, tweetID): url = 'https://twitter.com/{}/status/{}'.format(tweeter, tweetID) webPage = self.bot.moduleHandler.runActionUntilValue('fetch-url', url) soup = BeautifulSoup(webPage.body, 'lxml') tweet = soup.find(class_='permalink-tweet') user = tweet.find(class_='username').text tweetText = tweet.find(class_='tweet-text') tweetTimeText = tweet.find(class_='client-and-actions').text.strip() try: tweetTimeText = time.strftime('%Y/%m/%d %H:%M', time.strptime(tweetTimeText, '%I:%M %p - %d %b %Y')) except ValueError: pass tweetTimeText = re.sub(r'[\r\n\s]+', u' ', tweetTimeText) links = tweetText.find_all('a', {'data-expanded-url': True}) for link in links: link.string = ' ' + link['data-expanded-url'] embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'}) for link in embeddedLinks: link.string = ' ' + link['href'] text = string.unescapeXHTML(tweetText.text) text = re.sub('[\r\n]+', self.graySplitter, text) formatString = str(assembleFormattedText(A.normal[A.fg.gray['[{0}]'], A.bold[' {1}:'], ' {2}'])) return formatString.format(tweetTimeText, user, text), url
def link_parse(bot, args, sender, source): matches = parse.findall(args[0]) titles = [] for match in matches: if "gfycat" in match or ".webm" in match: continue if not dave.config.redis.exists("site:{}".format(match)): try: res = get(match, timeout=3, headers={'user-agent': 'irc bot (https://github.com/w4)'}) except BaseException as e: log.msg("Couldn't connect to host.", e) return # sometimes requests guesses the charset wrong if res.encoding == 'ISO-8859-1' and not 'ISO-8859-1' in \ res.headers.get('Content-Type', ''): res.encoding = res.apparent_encoding try: soup = BeautifulSoup(res.text, "html.parser") title = soup.title.string if title is not None: title = re.sub(r"(\r?\n|\r| )+", " ", title.strip()) title = title[:140] + (title[140:] and '...') dave.config.redis.setex("site:{}".format(match), 300, title) except BaseException as e: log.msg("Failed to grab title", e) return else: title = str(dave.config.redis.get("site:{}".format(match)), 'utf8') if title is not None: titles.append(assembleFormattedText(A.normal[title])) if titles: # remove duplicates titles = list(set(titles)) bot.msg(source, "Title: {}".format( assembleFormattedText(A.normal[", "]).join(titles)))
def test(self, bot, nick, channel, message): price = 0.00000000001212 dict_ = {} print price print type(price) dict_["hi"] = assembleFormattedText(A.fg.green[str(price)]) #dict_["hi"] = str(price) bot.sendMsg(channel, dict_["hi"])
def _fetch(self, j, short, mode, label): r = j[mode] data = [] t = A.normal[A.bold['{} {}: '.format(label, r[0]['rule']['name'])], '/'.join([r[0]['stage_a']['name'], r[0]['stage_b']['name']])] data.append(assembleFormattedText(t)) if not short: # include next maps now = int(time.time()) startTime = r[1]['startTime'] delta = startTime - now d = datetime.timedelta(seconds=delta) deltaStr = string.deltaTimeToString(d, resolution='m') t = A.normal[A.bold['{} {} in {}: '.format(label, r[1]['rule']['name'], deltaStr)], '/'.join([r[1]['stage_a']['name'], r[1]['stage_b']['name']])] data.append(assembleFormattedText(t)) return ' | '.join(data)
def _fetch(self, j, short, mode, label): r = j['modes'][mode] data = [] t = A.normal[A.bold['{} {}: '.format(label, r[0]['rule']['name'])], '/'.join(r[0]['maps'])] data.append(assembleFormattedText(t)) if not short: # include next maps now = int(time.time()) startTime = r[1]['startTime'] delta = startTime - now d = datetime.timedelta(seconds=delta) deltaStr = StringUtils.deltaTimeToString(d, resolution='m') t = A.normal[A.bold['{} {} in {}: '. format(label, r[1]['rule']['name'], deltaStr)], '/'.join(r[1]['maps'])] data.append(assembleFormattedText(t)) return ' | '.join(data)
def get_price(self, bot, nick, channel, msg): coin = msg.split(" ", 1)[0] try: message = {} for x in self.coin_data[coin]: r = requests.get("http://pubapi2.cryptsy.com/api.php?method=singlemarketdata&marketid={}".format(x)).json() label = r["return"]["markets"][coin.upper()]["label"].encode("utf-8") price = r["return"]["markets"][coin.upper()]["lasttradeprice"] try: if price > self.coin_data[label]["last"]: message[label] = assembleFormattedText(A.fg.green["".encode("utf-8")+str(price)]) else: message[label] = assembleFormattedText(A.fg.red[""+str(price)]) except Exception as e: self.coin_data[label] = {} message[label] = assembleFormattedText(A.fg.red[""+str(price)]) self.coin_data[label]["last"] = price bot.sendMsg(channel, ("(Cryptsy) "+"".join([(assembleFormattedText(A.bold["["])+"%s] %s " % (k,v)) for k,v in message.iteritems()])).encode("utf-8")) except Exception as e: return
def privmsg(self, user, channel, message): # If someone addresses the bot directly, respond in the same way. if channel == self.nickname: if message.lower().startswith("who ") and len(message.split(" ")) > 1: who_channel = message.split(" ")[1] if ("#" + who_channel) in self.ircUsers.users and who_channel not in self.ircUsers.users: who_channel = "#" + who_channel self.sendWhoToUser(who_channel, user) elif (message.lower().startswith("msg ") or message.lower().startswith("m ")) and len( message.split(" ")) > 2: msg_user = message.split(" ", 2)[1] msg_message = message.split(" ", 2)[2] if not communicator.sendmessage(self, formatUsername(msg_user), "[%s] %s" % (formatUsername(user), msg_message.strip())): self.sendToUser(user, irc.assembleFormattedText( irc.attributes.normal["Unable to find user ", irc.attributes.bold[ msg_user], " on any relayed channel."])) else: self.sendToUser(user, self.privMsgResponse) self.sendToUser(user, irc.assembleFormattedText(irc.attributes.normal["Say ", irc.attributes.bold[ "WHO ", irc.attributes.underline["#channel"]], " to see who is on ", irc.attributes.underline[ "#channel"], " via a separate network."])) self.sendToUser(user, irc.assembleFormattedText(irc.attributes.normal["Say ", irc.attributes.bold[ "MSG ", irc.attributes.underline["user"], " ", irc.attributes.underline[ "message"]], " to send a private ", irc.attributes.underline[ "message"], " to ", irc.attributes.underline[ "user"], "."])) elif channel in self.channels: if message.lower().strip() in [".who", "!who", "?who", ".names", "!names", "?names", ".users", "!users", "?users"]: self.sendWhoToUser(channel, user) else: self.relay(channel, "[%s] %s" % (formatUsername(user), message)) if message.startswith(self.nickname + ':'): self.sayToChannel(channel, self.privMsgResponse + " -- Send a private message for more information.") # For consistency, if anyone responds to the bot's response: self.relay(channel, "[%s] %s" % (formatUsername(self.nickname), self.privMsgResponse + " -- Send a private message for more information."))
def link_parse(bot, args, sender, source): matches = parse.findall(args[0]) titles = [] for match in matches: if not dave.config.redis.exists("site:{}".format(match)): try: res = get( match, timeout=3, headers={'user-agent': 'irc bot (https://github.com/w4)'}) except BaseException as e: log.msg("Couldn't connect to host.", e) return # sometimes requests guesses the charset wrong if res.encoding == 'ISO-8859-1' and not 'ISO-8859-1' in \ res.headers.get('Content-Type', ''): res.encoding = res.apparent_encoding soup = BeautifulSoup(res.text, "html.parser") title = soup.title.string if title is not None: title = re.sub(r"(\r?\n|\r| )+", " ", title.strip()) title = title[:140] + (title[140:] and '...') dave.config.redis.setex("site:{}".format(match), 300, title) else: title = str(dave.config.redis.get("site:{}".format(match)), 'utf8') if title is not None: titles.append(assembleFormattedText(A.bold[title])) if titles: # remove duplicates titles = list(set(titles)) bot.msg( source, "Title: {}".format( assembleFormattedText(A.normal[", "]).join(titles)))
def FollowTwitch(self, channel, message): # Heavily based on Didero's DideRobot code for the same # https://github.com/Didero/DideRobot/blob/06629fc3c8bddf8f729ce2d27742ff999dfdd1f6/commands/urlTitleFinder.py#L37 # TODO: other stats? chanData = {} channelOnline = False twitchHeaders = [('Accept', 'application/vnd.twitchtv.v2+json')] webPage = WebUtils.fetchURL(u'https://api.twitch.tv/kraken/streams/{0}'.format(channel), twitchHeaders) streamData = json.loads(webPage.body) if 'stream' in streamData and streamData['stream'] is not None: chanData = streamData['stream']['channel'] channelOnline = True elif 'error' not in streamData: webPage = WebUtils.fetchURL(u'https://api.twitch.tv/kraken/channels/{0}'.format(channel), twitchHeaders) chanData = json.loads(webPage.body) if len(chanData) > 0: if channelOnline: channelInfo = assembleFormattedText(A.fg.green['']) + '{0}'.format(chanData['display_name']) + assembleFormattedText(A.normal['']) else: channelInfo = assembleFormattedText(A.fg.red['']) + '{0}'.format(chanData['display_name']) + assembleFormattedText(A.normal['']) channelInfo += u' "{0}"'.format(re.sub('[\r\n]+', self.graySplitter, chanData['status'].strip())) if chanData['game'] is not None: channelInfo += assembleFormattedText(A.normal[A.fg.gray[', playing '], '{0}'.format(chanData['game'])]) if chanData['mature']: channelInfo += assembleFormattedText(A.normal[A.fg.lightRed[' [Mature]']]) if channelOnline: channelInfo += assembleFormattedText(A.normal[A.fg.green[' (Live with {0:,d} viewers)'.format(streamData['stream']['viewers'])]]) else: channelInfo += assembleFormattedText(A.normal[A.fg.red[' (Offline)']]) return IRCResponse(ResponseType.Say, channelInfo, message.ReplyTo)
def quote(bot, args, sender, source): query = dave.config.session.query(Quote) if not query.count(): bot.reply(source, sender, "No quotes found.") return row = query.offset(random.randrange(query.count())).first() bot.reply( source, sender, assembleFormattedText(A.normal[A.bold[row.quote], " by ", (row.attributed or row.added_by)]))
def current_conditions(self, channel, nick, msg, args): query = ' '.join(args['query'].split(' ')).lstrip() if 'wunderground_key' not in self.bot.plugin_config: logging.debug('configure weather plugin') self.bot.msg(channel, 'Weather plugin not configured') return False try: args = {'key': self.bot.plugin_config['wunderground_key'], 'query': query} r = requests.get(API_URL % args) r.raise_for_status() data = r.json()['current_observation'] except: logging.exception('Caught exception while searching for weather') self.bot.msg(channel, 'Cannot find weather for %s' % query) return False response = assembleFormattedText(A.bold[data['display_location']['full'].encode('UTF-8')]) response += assembleFormattedText(A.normal[" (%s): " % query]) response += "%(weather)s, %(temperature_string)s, Humidity: %(relative_humidity)s, %(observation_time)s" % data self.bot.msg(channel, response.encode('UTF-8'))
def last_tweet(self, channel, nick, msg, args): if 'twitter_api' not in self.bot.plugin_config: logging.debug('configure twitter plugin') self.bot.msg(channel, 'Twitter plugin not configured') return False creds = self.bot.plugin_config['twitter_api'] auth = twitter_oauth(creds['access_token'], creds['access_token_secret'], creds['consumer_key'], creds['consumer_secret']) t = twitter_api(auth=auth) try: user = t.users.lookup(screen_name=args['user'])[0] except TwitterHTTPError: logging.exception('Caught twitter api exception:') self.bot.msg('Error retreiving tweet from %s' % args['user']) return text = user['status']['text'].encode('UTF-8') screen_name = args['user'].encode('UTF-8') formatted_msg = assembleFormattedText(A.bold[screen_name]) formatted_msg += assembleFormattedText(A.normal[" tweets: "]) + text self.bot.msg(channel, formatted_msg)
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0 or len(message.ParameterList) > 2: return IRCResponse(ResponseType.Say, self.help, message.ReplyTo) subreddit = message.ParameterList[0].lower() if len(message.ParameterList) == 2: try: topRange = int(message.ParameterList[1]) if topRange < 0: return IRCResponse(ResponseType.Say, "The range should be a positive integer!", message.ReplyTo) except ValueError: return IRCResponse(ResponseType.Say, "The range should be a positive integer!", message.ReplyTo) else: topRange = 100 url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}" url = url.format(subreddit, random.randint(0, topRange)) response = WebUtils.fetchURL(url, self.headers) jsonResponse = json.loads(response.body) images = jsonResponse["data"] if not images: return IRCResponse( ResponseType.Say, "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)".format( subreddit ), message.ReplyTo, ) image = random.choice(images) data = [] if image["title"] is not None: data.append(image["title"]) if image["nsfw"]: data.append(u"\x034\x02NSFW!\x0F") if image["animated"]: data.append(u"\x032\x02Animated!\x0F") if "gifv" in image: data.append(image["gifv"]) else: data.append(image["link"]) graySplitter = assembleFormattedText(A.normal[" ", A.fg.gray["|"], " "]) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0 or len(message.ParameterList) > 2: return IRCResponse(ResponseType.Say, self.help, message.ReplyTo) subreddit = message.ParameterList[0].lower() if len(message.ParameterList) == 2: try: if len(message.ParameterList[1]) < 20: topRange = int(message.ParameterList[1]) else: raise ValueError if topRange < 0: raise ValueError except ValueError: return IRCResponse(ResponseType.Say, "The range should be a positive integer!", message.ReplyTo) else: topRange = 100 url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}" url = url.format(subreddit, random.randint(0, topRange)) response = WebUtils.fetchURL(url, self.headers) jsonResponse = json.loads(response.body) images = jsonResponse['data'] if not images: return IRCResponse(ResponseType.Say, "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)" .format(subreddit), message.ReplyTo) image = random.choice(images) data = [] if 'title' in image and image['title'] is not None: data.append(image['title']) if 'nsfw' in image and image['nsfw']: data.append(u'\x034\x02NSFW!\x0F') if 'animated' in image and image['animated']: data.append(u'\x032\x02Animated!\x0F') if 'gifv' in image: data.append(image['gifv']) else: data.append(image['link']) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
def crypto(bot, args, sender, source): cryptocurrency = args[0].upper() cryptoKey = cryptocurrency.lower() currency = (args[1] or 'usd').upper() currencyKey = currency.lower() p = preev(cryptocurrency, currency) if p == 'Not found.': # Invalid currency. bot.reply(source, sender, "Preev doesn't support that currency.") return if 'usd' in p[cryptoKey] \ and currencyKey not in p[cryptoKey] and 'usd' in p[currencyKey]: # We were given the exchange rate for currency -> usd but not currency -> crypto multiplier = 1 / decimal.Decimal(list(p[currencyKey]['usd'].values())[0]['last']) currencyKey = 'usd' else: multiplier = decimal.Decimal(1) total_volume = sum( float(market['volume']) for market in p[cryptoKey][currencyKey].values() ) avg = sum( float(market['last']) * float(market['volume']) / total_volume for market in p[cryptoKey][currencyKey].values() ) prices = assembleFormattedText(A.normal[ A.bold['{} -> {}'.format(cryptocurrency, currency)], ': ', ', '.join([ u'{}: {}'.format( market, babel.numbers.format_currency(decimal.Decimal(data['last']) * multiplier, currency) ) for market, data in p[cryptoKey][currencyKey].items() ]) ]) prices += u'. average: {}'.format( babel.numbers.format_currency(decimal.Decimal(avg) * multiplier, currency) ) bot.reply(source, sender, prices)
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0: return IRCResponse(ResponseType.Say, "You didn't give me any text to rainbow!", message.ReplyTo) outputMessage = '' for i, c in enumerate(message.Parameters): outputMessage += self.colours[i % len(self.colours)] + c outputMessage += assembleFormattedText(A.normal['']) return IRCResponse(ResponseType.Say, outputMessage, message.ReplyTo)
def speedtest(bot, args, sender, source): res = get("http://www.speedtest.net/result/{}".format(args[0]), timeout=3) soup = BeautifulSoup(res.text, "html.parser") download = soup.select(".share-speed.share-download p")[0].text upload = soup.select(".share-speed.share-upload p")[0].text ping = soup.select(".share-data.share-ping p")[0].text isp = soup.select(".share-data.share-isp p")[0].text bot.msg(source, assembleFormattedText(A.normal[ A.bold[str(isp)], ": " "Download: ", A.bold[str(download)], " ", "Upload: ", A.bold[str(upload)], " ", "Ping: ", A.bold[str(ping)] ]))
def speedtest(bot, args, sender, source): res = get("http://www.speedtest.net/result/{}".format(args[0]), timeout=3) soup = BeautifulSoup(res.text, "html.parser") download = soup.select(".share-speed.share-download p")[0].text upload = soup.select(".share-speed.share-upload p")[0].text ping = soup.select(".share-data.share-ping p")[0].text isp = soup.select(".share-data.share-isp p")[0].text bot.msg( source, assembleFormattedText(A.normal[A.bold[str(isp)], ": " "Download: ", A.bold[str(download)], " ", "Upload: ", A.bold[str(upload)], " ", "Ping: ", A.bold[str(ping)]]))
def first_google_result(self, channel, nick, msg, args): if 'google_apis' not in self.bot.plugin_config: logging.error('Google apis not configured') self.bot.msg('Google search plugin not configured') headers = {'Referer': self.bot.plugin_config['google_apis']['referer']} try: r = requests.get(API_URL % args['query'].replace(' ', ' '), headers=headers) r.raise_for_status() except requests.exceptions.HTTPError: logging.exception("google search httperror: ") result = r.json()['responseData']['results'][0] title = result['titleNoFormatting'].encode('UTF-8') formatted_msg = assembleFormattedText(A.bold[title]) + ": " formatted_msg += result['url'].encode('UTF-8') self.bot.msg(channel, formatted_msg)
def add_quote(bot, args, sender, source): generated_uuid = str(uuid.uuid4()) quote = Quote(id=generated_uuid, quote=args[0], attributed=args[1], added_by=sender) dave.config.session.add(quote) bot.reply( source, sender, assembleFormattedText(A.normal["Successfully added quote: ", A.bold[args[0]], " by ", (args[1] or sender)])) bot.msg( sender, "You can remove this quote later using \"dave dq {}\"".format( generated_uuid))
def urbandictionary(bot, args, sender, source): result = int(args[0].strip()) - 1 if args[0] else 0 query = args[1].strip().lower() key = "urban:{}:{}".format(query, result) if dave.config.redis.exists(key): bot.reply(source, sender, dave.config.redis.get(key).decode('utf-8')) return if not dave.config.redis.exists("urban_query:{}".format(query)): url = "https://mashape-community-urban-dictionary.p.mashape.com/define?term={}".format(quote_plus(query)) r = requests.get(url, headers={ "X-Mashape-Key": dave.config.config["api_keys"]["mashape"], "Accept": "text/plain" }) resp = r.json() dave.config.redis.setex("urban_query:{}".format(query), 86400, pickle.dumps(resp)) else: resp = pickle.loads(dave.config.redis.get("urban_query:{}".format(query))) print(resp) if len(resp["list"]) > result: res = resp["list"][result] definition = re.sub(r"\r?\n|\r", "", res["definition"].strip()) if len(definition) > 200: definition = definition[:197] + "..." definition = assembleFormattedText(A.normal[A.bold[str(res["word"])], ": {} [by {}, +{}/-{}] [more at {}]".format( definition, res["author"], res["thumbs_up"], res["thumbs_down"], res["permalink"] )]) dave.config.redis.setex(key, 86400, definition) bot.reply(source, sender, definition) else: bot.reply(source, sender, "There are no entries for: {} at position {}".format( query, result ))
def subreddit(bot, args, sender, source): """Ran whenever a subreddit is mentioned""" if dave.config.redis.exists("reddit:subreddit:mentioned:{}:{}".format( args[0], source)): # if this subreddit was mentioned in the last x seconds (see the setex below), # don't spam info about it return if not dave.config.redis.exists("reddit:subreddit:{}".format(args[0])): req = get("https://reddit.com/r/{}/about.json".format(args[0]), headers={'user-agent': 'irc bot (https://github.com/w4)'}) if req.status_code != 200: return if "/search.json" in req.url: # 404'd, reddit redirected us to the search page because they couldn't find # the user. return req = req.json() dave.config.redis.setex("reddit:subreddit:{}".format(args[0]), 600, pickle.dumps(req)) else: req = pickle.loads( dave.config.redis.get("reddit:subreddit:{}".format(args[0]))) resp = req["data"] # don't give info about this user again in this channel for 300 seconds dave.config.redis.setex( "reddit:subreddit:mentioned:{}:{}".format(args[0], source), 300, 1) bot.msg( source, assembleFormattedText(A.normal[ A.bold[A.fg.lightRed["[NSFW] "]] if resp["over18"] else "", A.bold[resp["title"]], " ({}), a community for {}. {} subscribers, {} browsing right now." .format( resp["display_name_prefixed"], naturaldelta(datetime.utcnow().timestamp() - resp["created"]), intcomma(resp["subscribers"]), intcomma(resp["accounts_active"]))]))
def subreddit(bot, args, sender, source): """Ran whenever a subreddit is mentioned""" if dave.config.redis.exists("reddit:subreddit:mentioned:{}:{}".format(args[0], source)): # if this subreddit was mentioned in the last x seconds (see the setex below), # don't spam info about it return if not dave.config.redis.exists("reddit:subreddit:{}".format(args[0])): req = get("https://reddit.com/r/{}/about.json".format(args[0]), headers={'user-agent': 'irc bot (https://github.com/w4)'}) if req.status_code != 200: return if "/search.json" in req.url: # 404'd, reddit redirected us to the search page because they couldn't find # the user. return req = req.json() dave.config.redis.setex("reddit:subreddit:{}".format(args[0]), 600, pickle.dumps(req)) else: req = pickle.loads(dave.config.redis.get("reddit:subreddit:{}".format(args[0]))) resp = req["data"] # don't give info about this user again in this channel for 300 seconds dave.config.redis.setex("reddit:subreddit:mentioned:{}:{}".format(args[0], source), 300, 1) bot.msg(source, assembleFormattedText( A.normal[ A.bold[A.fg.lightRed["[NSFW] "]] if resp["over18"] else "", A.normal[resp["title"]], " ({}), a community for {}. {} subscribers, {} browsing right now.".format( resp["display_name_prefixed"], naturaldelta(datetime.utcnow().timestamp() - resp["created"]), intcomma(resp["subscribers"]), intcomma(resp["accounts_active"]) ) ] ))
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0: return IRCResponse(ResponseType.Say, "You didn't give me any text to rainbow!", message.ReplyTo) outputMessage = u'' if message.Command == 'rainbow': for i, c in enumerate(message.Parameters): outputMessage += self.colours[i % len(self.colours)] + c else: for i, c in enumerate(message.Parameters): outputMessage += self.bgcolours[i % len(self.bgcolours)] + c outputMessage += assembleFormattedText(A.normal['']) return IRCResponse(ResponseType.Say, outputMessage, message.ReplyTo)
def post(bot, args, sender, source): """Ran whenever a reddit post is sent""" if dave.config.redis.exists("reddit:post:mentioned:{}:{}".format( args[0], source)): # if this post was mentioned in the last x seconds (see the setex below), # don't spam info about it return if not dave.config.redis.exists("reddit:post:{}".format(args[0])): req = get("https://reddit.com/{}.json?limit=1".format(args[0]), headers={'user-agent': 'irc bot (https://github.com/w4)'}) if req.status_code != 200: return req = req.json() dave.config.redis.setex("reddit:post:{}".format(args[0]), 200, pickle.dumps(req)) else: req = pickle.loads( dave.config.redis.get("reddit:post:{}".format(args[0]))) resp = req[0]["data"]["children"][0]["data"] dave.config.redis.setex( "reddit:post:mentioned:{}:{}".format(args[0], source), 300, 1) bot.msg( source, assembleFormattedText(A.normal[ A.bold[A.fg.lightRed["[NSFW] "]] if resp["over_18"] else "", A.bold[resp["title"][:75] + (resp["title"][75:] and '...')], " by ", A.bold[resp["author"]], " (/r/{}) {} comments, {} points, posted {}".format( resp["subreddit"], intcomma(resp["num_comments"] ), intcomma(resp["score"]), naturaltime(datetime.utcnow().timestamp() - resp["created_utc"])), ]))
def youtubevideo(bot, args, sender, source): """Ran whenever a YouTube video is sent""" if not dave.config.redis.exists("youtube:{}".format(args[0])): req = get("{}&id={}".format(BASE_URL, args[0]), headers={'user-agent': 'irc bot (https://github.com/w4)'}) if req.status_code != 200: print("{} from YouTube API: {}", req.status_code, req.json()) return req = req.json() if not req["pageInfo"]["totalResults"]: bot.msg(source, "That video doesn't exist.") return dave.config.redis.setex("youtube:{}".format(args[0]), 400, pickle.dumps(req)) else: req = pickle.loads(dave.config.redis.get("youtube:{}".format(args[0]))) resp = req["items"][0] bot.msg(source, assembleFormattedText( A.normal[ A.bold[resp["snippet"]["title"]], " ({}) by {} uploaded {}. {} views, +{}/-{}.".format( str(isodate.parse_duration(resp["contentDetails"]["duration"])), resp["snippet"]["channelTitle"], naturaltime( datetime.now(timezone.utc) - isodate.parse_datetime(resp["snippet"]["publishedAt"]) ), intcomma(resp["statistics"]["viewCount"]), intcomma(resp["statistics"]["likeCount"]), intcomma(resp["statistics"]["dislikeCount"]) ) ] ))
def youtubevideo(bot, args, sender, source): """Ran whenever a YouTube video is sent""" if not dave.config.redis.exists("youtube:{}".format(args[0])): req = get("{}&id={}".format(BASE_URL, args[0]), headers={'user-agent': 'irc bot (https://github.com/w4)'}) if req.status_code != 200: bot.msg( source, "Bad response from YouTube API: {}".format(req.status_code)) return req = req.json() if not req["pageInfo"]["totalResults"]: bot.msg(source, "That video doesn't exist.") return dave.config.redis.setex("youtube:{}".format(args[0]), 400, pickle.dumps(req)) else: req = pickle.loads(dave.config.redis.get("youtube:{}".format(args[0]))) resp = req["items"][0] bot.msg( source, assembleFormattedText(A.normal[ A.bold[resp["snippet"]["title"]], " ({}) by {} uploaded {}. {} views, +{}/-{}.".format( str(isodate.parse_duration(resp["contentDetails"]["duration"]) ), resp["snippet"]["channelTitle"], naturaltime( datetime.now(timezone.utc) - isodate.parse_datetime(resp["snippet"]["publishedAt"]) ), intcomma(resp["statistics"]["viewCount"] ), intcomma(resp["statistics"]["likeCount"]), intcomma(resp["statistics"]["dislikeCount"]))]))
def _stringifyTweet(self, tweet): """ turn a tweet object into a nice string for us to send to IRC @type tweet: dict[str, T/str] """ retweet = None # get the original tweet if this is a retweet if 'retweeted_status' in tweet: retweet = tweet tweet = retweet['retweeted_status'] tweetText = StringUtils.unescapeXHTML(tweet['text']) tweetText = re.sub('[\r\n]+', StringUtils.graySplitter, tweetText) for url in tweet['entities']['urls']: tweetText = tweetText.replace(url['url'], url['expanded_url']) timestamp = parser.parse(tweet['created_at']) timeString = timestamp.strftime('%A, %Y-%m-%d %H:%M:%S %Z') delta = datetime.datetime.utcnow() - timestamp.replace(tzinfo=None) deltaString = StringUtils.deltaTimeToString(delta) tweetText = u'{} (tweeted {}, {} ago)'.format(tweetText, timeString, deltaString) if retweet is None: user = tweet['user']['screen_name'] else: user = retweet['user']['screen_name'] tweetText = 'RT {}: {}'.format(tweet['user']['screen_name'], tweetText) link = u'https://twitter.com/{}/status/{}'.format( tweet['user']['screen_name'], tweet['id_str']) link = WebUtils.shortenGoogl(link) formatString = unicode( assembleFormattedText(A.normal[A.bold['@{0}>'], ' {1} {2}'])) newTweet = formatString.format(user, tweetText, link) return newTweet
def user(bot, args, sender, source): if dave.config.redis.exists("reddit:user:mentioned:{}:{}".format( args[0], source)): # if this user was mentioned in the last x seconds (see the setex below), don't # spam info about them return if not dave.config.redis.exists("reddit:user:{}".format(args[0])): req = get("https://reddit.com/u/{}/about.json".format(args[0]), headers={'user-agent': 'irc bot (https://github.com/w4)'}) if req.status_code != 200: return req = req.json() dave.config.redis.setex("reddit:user:{}".format(args[0]), 600, pickle.dumps(req)) else: req = pickle.loads( dave.config.redis.get("reddit:user:{}".format(args[0]))) resp = req["data"] # don't give info about this user again in this channel for 300 seconds dave.config.redis.setex( "reddit:user:mentioned:{}:{}".format(args[0], source), 300, 1) bot.msg( source, assembleFormattedText(A.normal[ A.bold[resp["name"]], ", a redditor for {}. {} link karma, {} comment karma.".format( naturaldelta(datetime.utcnow().timestamp() - resp["created"]), intcomma(resp["link_karma"]), intcomma(resp["comment_karma"])), " Verified user." if resp["verified"] else "", " Reddit employee." if resp["is_employee"] else ""]))
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) != 1: return IRCResponse(ResponseType.Say, self.help, message.ReplyTo) subreddit = message.ParameterList[0].lower() url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}" url = url.format(subreddit, random.randint(0, 100)) response = WebUtils.fetchURL(url, self.headers) jsonResponse = json.loads(response.body) images = jsonResponse['data'] if not images: return IRCResponse( ResponseType.Say, "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)" .format(subreddit), message.ReplyTo) image = random.choice(images) data = [] if image['title'] is not None: data.append(image['title']) if image['nsfw']: data.append(u'\x034\x02NSFW!\x0F') if image['animated']: data.append(u'\x032\x02Animated!\x0F') data.append(image['link']) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
def find_quote(bot, args, sender, source): try: quotes = dave.config.session.query( Quote).filter((Quote.quote.op("~")(args[0])) | (Quote.attributed.op("~")(args[0])) | (Quote.added_by.op("~")(args[0]))).all() except SQLAlchemyError as e: bot.reply(source, sender, SQLAlchemyError.__str__(e)) return if len(quotes) == 0: bot.reply(source, sender, "No results for query.") if len(quotes) > 3: bot.reply(source, sender, "Your query yielded too many results ({}), here's a " \ "random sample:".format(len(quotes))) quotes = random.sample(quotes, 3) for quote in quotes: bot.reply( source, sender, assembleFormattedText( A.normal[A.bold[quote.quote], " by ", (quote.attributed or quote.added_by)]))
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) < 3: return IRCResponse(ResponseType.Say, self.help, message.ReplyTo) try: amount = float(message.ParameterList[0]) offset = 1 except ValueError: amount = 1.0 offset = 0 ccFrom = message.ParameterList[offset].upper() ccTo = message.ParameterList[offset+2:] ccTo = ",".join(ccTo) ccTo = ccTo.upper() url = "https://api.fixer.io/latest?base={}&symbols={}" url = url.format(ccFrom, ccTo) response = WebUtils.fetchURL(url) jsonResponse = json.loads(response.body) rates = jsonResponse['rates'] if not rates: return IRCResponse(ResponseType.Say, "Some or all of those currencies weren't recognized!", message.ReplyTo) data = [] for curr,rate in rates.iteritems(): data.append("{} {}".format(rate*amount, curr)) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
class Splatoon(BotCommand): def triggers(self): return ['splat'] def help(self, query): return "splat [regular/ranked/league/fest]" graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) def _fetch(self, j, short, mode, label): r = j[mode] data = [] t = A.normal[ A.bold['{} {}: '.format(label, r[0]['rule']['name'])], '/'.join([r[0]['stage_a']['name'], r[0]['stage_b']['name']])] data.append(assembleFormattedText(t)) if not short: # include next maps now = int(time.time()) startTime = r[1]['startTime'] delta = startTime - now d = datetime.timedelta(seconds=delta) deltaStr = string.deltaTimeToString(d, resolution='m') t = A.normal[ A.bold['{} {} in {}: '. format(label, r[1]['rule']['name'], deltaStr)], '/'.join([r[1]['stage_a']['name'], r[1]['stage_b']['name']])] data.append(assembleFormattedText(t)) return ' | '.join(data) def _regular(self, j, short): return self._fetch(j, short, 'regular', 'Regular') def _ranked(self, j, short): return self._fetch(j, short, 'gachi', 'Ranked') def _league(self, j, short): return self._fetch(j, short, 'league', 'League') def _fest(self, j, short): if j['splatfests']: pass elif not short: return 'No SplatFest is currently scheduled' def execute(self, message: IRCMessage): url = "https://splatoon2.ink/data/schedules.json" response = self.bot.moduleHandler.runActionUntilValue('fetch-url', url) j = response.json() if len(message.parameterList) < 1: # do everything data = [] data += filter(None, [self._regular(j, short=True)]) data += filter(None, [self._ranked(j, short=True)]) data += filter(None, [self._league(j, short=True)]) data += filter(None, [self._fest(j, short=True)]) return IRCResponse(ResponseType.Say, self.graySplitter.join(data), message.replyTo) else: subCommands = { 'regular': self._regular, 'ranked': self._ranked, 'league': self._league, 'fest': self._fest } subCommand = message.parameterList[0].lower() if subCommand in subCommands: return IRCResponse(ResponseType.Say, subCommands[subCommand](j, short=False), message.replyTo) else: return IRCResponse(ResponseType.Say, self.help(None), message.replyTo)
def spell(bot, user, channel, args): # msg = 'This is an example.' # bot.send_msg(channel, msg, length = 450) s = { 'Q': 0, 'W': 1, 'E': 2, 'R': 3, } scalings = { 'bonusattackdamage': 'Bonus AD', 'spelldamage': 'AP', 'attackdamage': 'AD', '@stacks': 'per Stack', '@dynamic.attackdamage': 'Dyn. AD', '@special.dariusr3': '+ 32/50/68 per Stack', 'health': 'HP', 'abilitypower': 'AP', } # easteregg if len(args) < 1: return if args[0].lower() in ['riottriggs', 'triggs', 'triggs390']: bot.send_msg(channel, 'Triggs has no skill.') return # /easteregg if len(args) < 2: return args[0] = lol_ddragon.normalize_name(args[0]) data = lol_ddragon.get_champion(args[0])['data'][args[0]] passive = data['passive'] data = data['spells'][s[args[1].upper()]] cost = data['resource'] text = data['tooltip'] effects = data['effectBurn'] for i, e in enumerate(effects): cost = cost.replace('{{ e' + str(i) + ' }}', str(e)) text = text.replace('{{ e' + str(i) + ' }}', str(e)) cost = cost.replace('{{ cost }}', data['costBurn']) cd = data['cooldownBurn'] range_ = data['rangeBurn'] for i, a in enumerate(data['vars']): scale = '' if a['link'] in scalings: scale = ' ' + scalings[a['link']] text = text.replace('{{ ' + a['key'] + ' }}', str(a['coeff']) + scale) text = strip_tags(text) # msg = '[' + args[0] + ' | ' + args[1].upper() + ' | ' + data['name'] + '] ' # msg += '[Cost: ' + cost + '] [CD: ' + cd + '] [Range: ' + range_ + '] ' # msg += '[' + text + ']' msg = assembleFormattedText( A.normal['[', A.bold[args[0]], ' | ', A.bold[args[1].upper()], ' | ', A.bold[str(data['name'])], '] [', A.bold['Cost: '], cost + '] [', A.bold['CD: '], cd + '] [', A.bold['Range: '], range_ + '] [' + text + ']']) bot.send_msg(channel, str(msg))
class Rainbow(CommandInterface): triggers = ['rainbow', 'rrainbow'] help = 'rainbow <text> - outputs the specified text with rainbow colours; rrainbow uses background colours' colours = [ assembleFormattedText(A.fg.lightRed['']), #assembleFormattedText(A.fg.orange['']), assembleFormattedText(A.fg.yellow['']), assembleFormattedText(A.fg.lightGreen['']), assembleFormattedText(A.fg.lightCyan['']), assembleFormattedText(A.fg.lightBlue['']), assembleFormattedText(A.fg.lightMagenta['']), ] bgcolours = [ assembleFormattedText(A.bg.red['']), assembleFormattedText(A.bg.orange['']), assembleFormattedText(A.bg.yellow['']), assembleFormattedText(A.bg.green['']), assembleFormattedText(A.bg.cyan['']), assembleFormattedText(A.bg.blue['']), assembleFormattedText(A.bg.magenta['']), ] def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0: return IRCResponse(ResponseType.Say, "You didn't give me any text to rainbow!", message.ReplyTo) outputMessage = u'' if message.Command == 'rainbow': for i, c in enumerate(message.Parameters): outputMessage += self.colours[i % len(self.colours)] + c else: for i, c in enumerate(message.Parameters): outputMessage += self.bgcolours[i % len(self.bgcolours)] + c outputMessage += assembleFormattedText(A.normal['']) return IRCResponse(ResponseType.Say, outputMessage, message.ReplyTo)
def execute(self, message): """ @type message: IRCMessage """ subString = self._mangleEscapes(message.Parameters) try: segments = list(self._parseSubcommandTree(subString)) except UnbalancedBracesException as e: red = assembleFormattedText(A.fg.lightRed['']) normal = assembleFormattedText(A.normal['']) error = subString[:e.column] + red + subString[ e.column] + normal + subString[e.column + 1:] error = self._unmangleEscapes(error, False) return [ IRCResponse(ResponseType.Say, u"Sub Error: {}".format(e.message), message.ReplyTo), IRCResponse(ResponseType.Say, error, message.ReplyTo) ] prevLevel = -1 responseStack = [] extraVars = {} metadata = {} for segment in segments: (level, command, start, end) = segment # We've finished executing subcommands at the previous depth, # so replace subcommands with their output at the current depth if level < prevLevel: command = self._substituteResponses(command, responseStack, level, extraVars, start) # Build a new message out of this segment inputMessage = IRCMessage(message.Type, message.User.String, message.Channel, self.bot.commandChar + command.lstrip(), self.bot, metadata=metadata) # Execute the constructed message if inputMessage.Command.lower( ) in self.bot.moduleHandler.mappedTriggers: response = self.bot.moduleHandler.mappedTriggers[ inputMessage.Command.lower()].execute(inputMessage) """@type : IRCResponse""" else: return IRCResponse( ResponseType.Say, u"'{}' is not a recognized command trigger".format( inputMessage.Command), message.ReplyTo) # Push the response onto the stack responseStack.append((level, response.Response, start, end)) # Update the extraVars dict extraVars.update(response.ExtraVars) metadata = self._recursiveMerge(metadata, response.Metadata) prevLevel = level responseString = self._substituteResponses(subString, responseStack, -1, extraVars, -1) responseString = self._unmangleEscapes(responseString) return IRCResponse(ResponseType.Say, responseString, message.ReplyTo, extraVars=extraVars, metadata=metadata)
def execute(self, message): """ @type message: IRCMessage """ if len(message.ParameterList) == 0: return IRCResponse( ResponseType.Say, "You didn't give a word! Usage: {0}".format(self.help), message.ReplyTo) search = urllib.quote(message.Parameters) url = 'http://api.urbandictionary.com/v0/define?term={0}'.format( search) webPage = WebUtils.fetchURL(url) response = json.loads(webPage.body) if len(response['list']) == 0: return IRCResponse( ResponseType.Say, "No entry found for '{0}'".format(message.Parameters), message.ReplyTo) graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' ']) defn = response['list'][0] word = defn['word'] definition = defn['definition'] definition = graySplitter.join( [s.strip() for s in definition.strip().splitlines() if s]) example = defn['example'] example = graySplitter.join( [s.strip() for s in example.strip().splitlines() if s]) author = defn['author'] up = defn['thumbs_up'] down = defn['thumbs_down'] more = 'http://{}.urbanup.com/'.format(word.replace(' ', '-')) if word.lower() != message.Parameters.lower(): word = "{0} (Contains '{1}')".format(word, message.Parameters) defFormatString = unicode( assembleFormattedText(A.normal[A.bold["{0}:"], " {1}"])) exampleFormatString = unicode( assembleFormattedText(A.normal[A.bold["Example(s):"], " {0}"])) byFormatString = unicode( assembleFormattedText(A.normal["{0}", graySplitter, A.fg.lightGreen["+{1}"], A.fg.gray["/"], A.fg.lightRed["-{2}"], graySplitter, "More defs: {3}"])) responses = [ IRCResponse(ResponseType.Say, defFormatString.format(word, definition), message.ReplyTo), IRCResponse(ResponseType.Say, exampleFormatString.format(example), message.ReplyTo), IRCResponse(ResponseType.Say, byFormatString.format(author, up, down, more), message.ReplyTo) ] return responses