def get_video_description(video_id): global time_last_request time_elapsed = time.time() - time_last_request if time_elapsed > 10: time_last_request = time.time() else: #return "This looks like a YouTube video. However, the YT api have been called too much, I'm sorry I won't be able to fetch details for you." return None json = requests.get(api_url.format(video_id, dev_key)).json() if json.get('error'): if json['error']['code'] == 403: return err_no_api else: return data = json['items'] snippet = data[0]['snippet'] statistics = data[0]['statistics'] content_details = data[0]['contentDetails'] out = '\x02{}\x02'.format(snippet['title']) if not content_details.get('duration'): return out length = isodate.parse_duration(content_details['duration']) out += ' - length \x02{}\x02'.format(timeformat.format_time(int(length.total_seconds()), simple=True)) total_votes = float(statistics['likeCount']) + float(statistics['dislikeCount']) if total_votes != 0: # format likes = pluralize(int(statistics['likeCount']), "like") dislikes = pluralize(int(statistics['dislikeCount']), "dislike") percent = 100 * float(statistics['likeCount']) / total_votes likes = parse("$(dark_green)" + likes + "$(clear)") dislikes = parse("$(dark_red)" + dislikes + "$(clear)") out += ' - {}, {} (\x02{:.1f}\x02%)'.format(likes, dislikes, percent) if 'viewCount' in statistics: views = int(statistics['viewCount']) out += ' - \x02{:,}\x02 view{}'.format(views, "s"[views == 1:]) uploader = snippet['channelTitle'] upload_time = time.strptime(snippet['publishedAt'], "%Y-%m-%dT%H:%M:%S.000Z") out += ' - \x02{}\x02 on \x02{}\x02'.format(uploader, time.strftime("%Y.%m.%d", upload_time)) if 'contentRating' in content_details: out += ' - \x034NSFW\x02' # return re.sub( # r'(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))', # '[URL]', out) return out.replace("youtu", "you tu") #nup. No spam please
def check_reminders(bot, async_call, db): current_time = datetime.now() for reminder in reminder_cache: network, remind_time, added_time, user, message = reminder if remind_time <= current_time: if network not in bot.connections: # connection is invalid yield from add_reminder(async_call, db, network, remind_time, user) yield from load_cache(async_call, db) continue conn = bot.connections[network] if not conn.ready: return remind_text = colors.parse(time_since(added_time, count=2)) alert = colors.parse("{}, you have a reminder from $(b){}$(clear) ago!".format(user, remind_text)) conn.message(user, alert) conn.message(user, '"{}"'.format(message)) delta = (remind_time-added_time).seconds if delta > (30*60): late_time = time_since(remind_time, count=2) late = "(I'm sorry for delivering this message $(b){}$(clear) late," \ " it seems I was unable to deliver it on time)".format(late_time) conn.message(user, colors.parse(late)) yield from delete_reminder(async_call, db, network, remind_time, user) yield from load_cache(async_call, db)
def pronounce(text, event): """<word> - Returns instructions on how to pronounce <word> with an audio example.""" lookup = PronounciationLookupRequest(text) try: audio_response = list(lookup.get_filtered_results())[:5] except WordNotFound: return colors.parse( "Sorry, I don't know how to pronounce $(b){}$(b).").format(text) except WordnikAPIError as e: event.reply(e.user_msg()) raise out = colors.parse("$(b){}$(b): ").format(text) out += " • ".join([i['raw'] for i in audio_response]) audio_lookup = AudioLookupRequest(text) try: audio_response = audio_lookup.first() except WordNotFound: pass except WordnikAPIError as e: event.reply(e.user_msg()) raise else: url = web.try_shorten(audio_response['fileUrl']) out += " - {}".format(url) return out
def format_output(item, show_url=False): """ takes a reddit post and returns a formatted string """ item["title"] = formatting.truncate(item["title"], 70) item["link"] = short_url.format(item["id"]) raw_time = datetime.fromtimestamp(int(item["created_utc"])) item["timesince"] = timeformat.time_since(raw_time, count=1, simple=True) item["comments"] = formatting.pluralize_auto(item["num_comments"], 'comment') item["points"] = formatting.pluralize_auto(item["score"], 'point') if item["over_18"]: item["warning"] = colors.parse(" $(b, red)NSFW$(clear)") else: item["warning"] = "" if show_url: item["url"] = " - " + item["link"] else: item["url"] = "" return colors.parse( "$(b){title} : {subreddit}$(b) - {comments}, {points}" " - $(b){author}$(b) {timesince} ago{url}{warning}").format(**item)
def prev(text, reply, message): tvm = pytvmaze.TVMaze() try: show = tvm.get_show(show_name=text) if show.previous_episode is not None: message( colors.parse( '$(red)Previous Episode:$(clear) {} (S{}E{}) $(red)// Airing on:$(clear) {} $(red)//$(clear) {}' ).format(show.previous_episode.title, show.previous_episode.season_number, show.previous_episode.episode_number, show.previous_episode.airdate, show.previous_episode.summary)) message( colors.parse('$(red)More at:$(clear) {}').format( show.previous_episode.url)) else: message( colors.parse( '$(white, red){} does not have a previous episode$(clear)' ).format(show)) pass except Exception as e: message(colors.parse('$(white, red)Show Not Found$(clear)')) pass
def mcping(text): """<server[:port]> - gets info about the Minecraft server at <server[:port]>""" try: server = MinecraftServer.lookup(text) except (IOError, ValueError) as e: return str(e) try: s = server.status() # type: PingResponse except socket.gaierror: return "Invalid hostname" except socket.timeout: return "Request timed out" except ConnectionRefusedError: return "Connection refused" except ConnectionError: return "Connection error" except (IOError, ValueError) as e: return "Error pinging server: {}".format(e) if isinstance(s.description, dict): description = format_colors(" ".join(s.description["text"].split())) else: description = format_colors(" ".join(s.description.split())) output_format = colors.parse( "{}$(clear) - $(bold){}$(clear) - $(bold){:.1f}ms$(clear) - $(bold){}/{}$(clear) players" ) return output_format.format( description, s.version.name, s.latency, s.players.online, s.players.max ).replace("\n", colors.parse("$(clear) - "))
def attack(self, target, move): #TODO: add move type diff stuff here... #get move. by index or name, then put it in `move` replacing the key... text = '' if self.fainted: return colors.parse('$(dred){} is fainted and cannot battle'.format(self.name)) if type(move) is str: for i, v in enumerate(self.moves): if v.name.lower() == move.lower(): move = v break else: move = self.moves[move] assert type(move) is Move, "Move {} not found".format(move) #damage calc and handling #lvldiff = ((me.power/target.power)*CONF.lvl_bias + 1)/1+conf.lvl_bias#alt lvl calc formula damage = (self.level / target.level) * CONF.lvl_bias * move.effectiveness(target) target.hp -= damage text += "{} used {} on {}\n".format(self.name, move.name, target.name) if move.typeCheck(target) > 1: text += colors.parse("$(blue)It was super effective! ") elif move.typeCheck(target) < 1: text += colors.parse("$(yellow)It wasn't very effective ") text += "Damage: {}, {}'s hp: {:0.2f}".format(damage, target.name, target.hp) text += target.check() #exp handling and check if target.fainted: exp = (target.level / self.level) * (target.level * CONF.lvl_bias) self.level += exp text += "+{:0.2f} EXP! level is now {:0.2f}\n".format(exp, self.level) return text
async def check_reminders(bot, async_call, db): current_time = datetime.now() for reminder in reminder_cache: network, remind_time, added_time, user, message = reminder if remind_time <= current_time: if network not in bot.connections: # connection is invalid continue conn = bot.connections[network] if not conn.ready: return remind_text = colors.parse(time_since(added_time, count=2)) alert = colors.parse( "{}, you have a reminder from $(b){}$(clear) ago!".format( user, remind_text)) conn.message(user, alert) conn.message(user, '"{}"'.format(message)) delta = current_time - remind_time if delta > timedelta(minutes=30): late_time = time_since(remind_time, count=2) late = "(I'm sorry for delivering this message $(b){}$(clear) late," \ " it seems I was unable to deliver it on time)".format(late_time) conn.message(user, colors.parse(late)) await delete_reminder(async_call, db, network, remind_time, user) await load_cache(async_call, db)
def forecast(reply, db, event): """<location> - Gets forecast data for <location>.""" res, err = check_and_parse(event, db) if not res: return err location_data, fio = res daily_conditions = fio.get_daily()['data'] today, tomorrow, *three_days = daily_conditions[:5] today['name'] = 'Today' tomorrow['name'] = 'Tomorrow' for day_fc in (today, tomorrow): wind_speed = day_fc['windSpeed'] day_fc.update( wind_direction=bearing_to_card(day_fc['windBearing']), wind_speed_mph=wind_speed, wind_speed_kph=mph_to_kph(wind_speed), summary=day_fc['summary'].rstrip('.'), ) for fc_data in (today, tomorrow, *three_days): high = fc_data['temperatureHigh'] low = fc_data['temperatureLow'] fc_data.update( temp_high_f=round_temp(high), temp_high_c=round_temp(convert_f2c(high)), temp_low_f=round_temp(low), temp_low_c=round_temp(convert_f2c(low)), ) parts = [ ('High', "{temp_high_f:.0f}F/{temp_high_c:.0f}C"), ('Low', "{temp_low_f:.0f}F/{temp_low_c:.0f}C"), ('Humidity', "{humidity:.0%}"), ('Wind', "{wind_speed_mph:.0f}MPH/{wind_speed_kph:.0f}KPH {wind_direction}"), ] day_str = colors.parse("$(b){name}$(b): {summary}; ") + '; '.join( '{}: {}'.format(part[0], part[1]) for part in parts) url = web.try_shorten( 'https://darksky.net/forecast/{lat:.3f},{lng:.3f}'.format_map( location_data)) out_format = "{today_str} | {tomorrow_str} -- {place} - $(ul){url}$(clear)" reply( colors.parse(out_format).format( today_str=day_str.format_map(today), tomorrow_str=day_str.format_map(tomorrow), place=location_data['address'], url=url))
def weather(reply, db, triggered_prefix, event): """<location> - Gets weather data for <location>.""" res, err = check_and_parse(event, db) if not res: return err location_data, fio = res daily_conditions = fio.get_daily()['data'] current = fio.get_currently() today = daily_conditions[0] wind_speed = current['windSpeed'] today_high = today['temperatureHigh'] today_low = today['temperatureLow'] current.update( name='Current', wind_direction=bearing_to_card(current['windBearing']), wind_speed_mph=wind_speed, wind_speed_kph=mph_to_kph(wind_speed), summary=current['summary'].rstrip('.'), temp_f=round_temp(current['temperature']), temp_c=round_temp(convert_f2c(current['temperature'])), temp_high_f=round_temp(today_high), temp_high_c=round_temp(convert_f2c(today_high)), temp_low_f=round_temp(today_low), temp_low_c=round_temp(convert_f2c(today_low)), ) parts = [ ('Current', "{summary}, {temp_f}F/{temp_c}C"), ('High', "{temp_high_f}F/{temp_high_c}C"), ('Low', "{temp_low_f}F/{temp_low_c}C"), ('Humidity', "{humidity:.0%}"), ('Wind', "{wind_speed_mph:.0f}MPH/{wind_speed_kph:.0f}KPH {wind_direction}"), ] current_str = '; '.join( colors.parse('$(b){}$(b): {}$(clear)'.format(part[0], part[1])) for part in parts) url = web.try_shorten( 'https://darksky.net/forecast/{lat:.3f},{lng:.3f}'.format_map( location_data)) reply( colors.parse("{current_str} -- " "{place} - " "$(ul){url}$(clear) " "($(i)To get a forecast, use {cmd_prefix}fc$(i))").format( place=location_data['address'], current_str=current_str.format_map(current), url=url, cmd_prefix=triggered_prefix, ))
def word_usage(text, event): """<word> - Returns an example sentence showing the usage of <word>.""" lookup = ExamplesLookupRequest(text) try: example = lookup.random() except WordNotFound: return colors.parse( "I could not find any usage examples for $(b){}$(b).").format(text) except WordnikAPIError as e: event.reply(e.user_msg()) raise out = colors.parse("$(b){word}$(b): {text}").format(word=text, text=example['text']) return out
def factoid(content, match, chan, event, message, action): """<word> - shows what data is associated with <word>""" arg1 = "" if len(content.split()) >= 2: arg1 = content.split()[1] # split up the input split = match.group(1).strip().split(" ") factoid_id = split[0].lower() if len(split) >= 1: arguments = " ".join(split[1:]) else: arguments = "" if factoid_id in factoid_cache[chan]: data = factoid_cache[chan][factoid_id] result = data # factoid post-processors result = colors.parse(result) if arg1: result = result.replace("<user>", arg1) if result.startswith("<act>"): result = result[5:].strip() action(result) else: message(result)
def karma(text): """karma <reddituser> will return the information about the specified reddit username""" user = text url = user_url + "about.json" r = requests.get(url.format(user), headers=agent) if r.status_code != 200: return statuscheck(r.status_code, user) data = r.json() out = "$(b){}$(b) ".format(user) out += "$(b){:,}$(b) link karma and ".format(data['data']['link_karma']) out += "$(b){:,}$(b) comment karma | ".format( data['data']['comment_karma']) if data['data']['is_gold']: out += "has reddit gold | " if data['data']['is_mod']: out += "is a moderator | " if data['data']['has_verified_email']: out += "email has been verified | " out += "cake day is {} | ".format( datetime.fromtimestamp(data['data']['created_utc']).strftime('%B %d')) account_age = datetime.now() - datetime.fromtimestamp( data['data']['created']) age = account_age.days age_unit = "day" if age > 365: age //= 365 age_unit = "year" out += "redditor for {}.".format(pluralize(age, age_unit)) return colors.parse(out)
def get_sale_data(): """returns the next or current steam sale date""" request = requests.get(URL, headers=HEADERS) html = request.text soup = BeautifulSoup(html, 'html.parser') if soup.find("span", "sale-name"): sale_name = soup.find("span", "sale-name").string if soup.select_one("h3 > span.sale-unconfirmed"): status = soup.find("span", "sale-unconfirmed").contents[-1].strip() else: status = 'Confirmed' temp_date = soup.find("h3").contents[2].strip(' on') elif soup.find("div", "next-sale"): status = 'Active' sale_name = soup.find("h2").string temp_date = soup.find("h3").contents[0].strip('runs until') else: return "Something went wrong." temp_date = datetime.strptime(temp_date + " 15", "%d %B %Y %H") time = str(temp_date - datetime.now()).partition('.')[0] date = datetime.strftime(temp_date, "%B %d, %Y") return colors.parse( "$(bold){}$(clear) {} $(bold){}$(clear) ({} {}) [{}]".format( sale_name, 'on' if status != 'Active' else 'runs until', date, time, 'left' if status == 'Active' else 'from now', status))
def karma(text, reply): """<reddituser> - will return the information about the specified reddit username""" user = get_user(text) data = get_user_data('about.json', user, reply) data = data['data'] out = "$(b){}$(b) ".format(user) parts = [ "$(b){:,}$(b) link karma and $(b){:,}$(b) comment karma".format( data['link_karma'], data['comment_karma']) ] if data['is_gold']: parts.append("has reddit gold") if data['is_mod']: parts.append("moderates a subreddit") if data['has_verified_email']: parts.append("email has been verified") parts.append("cake day is {}".format( datetime.fromtimestamp(data['created_utc']).strftime('%B %d'))) account_age = datetime.now() - datetime.fromtimestamp(data['created']) age = account_age.days age_unit = "day" if age > 365: age //= 365 age_unit = "year" parts.append("redditor for {}.".format(pluralize_auto(age, age_unit))) return colors.parse(out + ' | '.join(parts))
def cake_day(text, reply): """<reddituser> - will return the cakeday for the given reddit username.""" user = get_user(text) url = user_url + "about.json" r = requests.get(url.format(user), headers=agent) try: r.raise_for_status() except HTTPError as e: reply(statuscheck(e.response.status_code, user)) raise if r.status_code != 200: return statuscheck(r.status_code, user) data = r.json() out = colors.parse("$(b){}'s$(b) ".format(user)) out += "cake day is {}, ".format( datetime.fromtimestamp(data['data']['created_utc']).strftime('%B %d')) account_age = datetime.now() - datetime.fromtimestamp( data['data']['created']) age = account_age.days age_unit = "day" if age > 365: age //= 365 age_unit = "year" out += "they have been a redditor for {}.".format( pluralize_auto(age, age_unit)) return out
def synonym(text, event): """<word> - Returns a list of synonyms for <word>.""" lookup = RelatedLookupRequest(text, 'synonym') try: data = lookup.first() except WordNotFound: return colors.parse( "Sorry, I couldn't find any synonyms for $(b){}$(b).").format(text) except WordnikAPIError as e: event.reply(e.user_msg()) raise out = colors.parse("$(b){}$(b): ").format(text) out += " • ".join(data['words']) return out
def submods(text, chan, conn, reply): """<subreddit> - prints the moderators of the specified subreddit.""" sub = get_sub(text) url = subreddit_url + "about/moderators.json" r = requests.get(url.format(sub), headers=agent) try: r.raise_for_status() except HTTPError as e: reply(statuscheck(e.response.status_code, 'r/' + sub)) raise if r.status_code != 200: return statuscheck(r.status_code, 'r/' + sub) data = r.json() moderators = [] for mod in data['data']['children']: username = mod['name'] # Showing the modtime makes the message too long for larger subs # if you want to show this information add modtime.days to out below modtime = datetime.now() - datetime.fromtimestamp(mod['date']) modtime = time_format(modtime.days) moderators.append("{} ({}{})".format(username, modtime[0], modtime[1])) pager = paginated_list(moderators) search_pages[conn.name][chan.casefold()] = pager page = pager.next() if len(pager) > 1: page[-1] += " .moremod" out = colors.parse("/r/$(b){}$(b) mods: ".format(sub)) page[0] = out + page[0] return page
def subinfo(text, reply): """<subreddit> - fetches information about the specified subreddit.""" sub = get_sub(text) url = subreddit_url + "about.json" r = requests.get(url.format(sub), headers=agent) try: r.raise_for_status() except HTTPError as e: reply(statuscheck(e.response.status_code, 'r/' + sub)) raise if r.status_code != 200: return statuscheck(r.status_code, 'r/' + sub) data = r.json() if data['kind'] == "Listing": return "It appears r/{} does not exist.".format(sub) name = data['data']['display_name'] title = data['data']['title'] nsfw = data['data']['over18'] subscribers = data['data']['subscribers'] active = data['data']['accounts_active'] sub_age = datetime.now() - datetime.fromtimestamp(data['data']['created']) age, age_unit = time_format(sub_age.days) out = "/r/$(b){}$(clear) - {} - a community for {}{}, there are {:,} subscribers and {:,} people online now.".format( name, title, age, age_unit, subscribers, active) if nsfw: out += " $(red)NSFW$(clear)" return colors.parse(out)
def horoscope(text, db, bot, nick, event): """[sign] - get your horoscope""" headers = {'User-Agent': bot.user_agent} sign, dontsave = parse_or_lookup(text, db, nick, event) if not sign: return params = {"sign": SIGN_MAP[sign]} try: request = requests.get(str(DAILY_URL), params=params, headers=headers) request.raise_for_status() except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: event.reply("Could not get horoscope: {}. URL Error".format(e)) raise try: horoscope_text = parse_page(request.text) except HoroscopeParseError: event.reply("Unable to parse horoscope posting") raise result = colors.parse("$(b){}$(b) {}").format(sign, horoscope_text) if text and not dontsave: set_sign(db, nick, sign) event.message(result)
def moderates(text, chan, conn, reply): """<username> - This plugin prints the list of subreddits a user moderates listed in a reddit users profile. Private subreddits will not be listed.""" user = get_user(text) r = requests.get(user_url.format(user) + "moderated_subreddits.json", headers=agent) try: r.raise_for_status() except HTTPError as e: reply(statuscheck(e.response.status_code, user)) raise if r.status_code != 200: return statuscheck(r.status_code, user) data = r.json() subs = data['data'] out = colors.parse( "$(b){}$(b) moderates these public subreddits: ".format(user)) pager = paginated_list([sub['sr'] for sub in subs]) search_pages[conn.name][chan.casefold()] = pager page = pager.next() if len(pager) > 1: page[-1] += " .moremod" page[0] = out + page[0] return page
def pre(text): """pre <query> -- searches scene releases using pre.corrupt.org""" try: headers = {'Accept-Language': 'en-US'} request = requests.get("https://pre.corrupt-net.org/search.php", params={"search": text}, headers=headers) request.raise_for_status() except requests.exceptions.HTTPError as e: return 'Unable to fetch results: {}'.format(e) split = request.text.partition('</tr><tr>') results = re.search( "<tr><td id\=\"rlstype\".*>(.*)</td><td.*> (.*)<span id\=\"rlsgroup\"><font color\='#C0C0C0'>(.*)</font>.*>(\d*F).*>([\d\.]*M).* (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})</td>", split[0], flags=re.IGNORECASE) if results is None: return "No results found." date = results.group(6) section = results.group(1) name = results.group(2) + results.group(3) size = results.group(5) files = results.group(4) # parse date/time date = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S") date_string = date.strftime("%d %b %Y") since = timeformat.time_since(date) return colors.parse( '$(orange){}$(clear) - $(blue){}$(clear) - $(red){}$(clear) - $(dgreen){}$(clear) - $(dgrey){}$(clear) $(purple)({} ago)$(clear)' .format(section, name, size, files, date_string, since))
async def remind(text, nick, chan, db, conn, event, async_call): """<1 minute, 30 seconds>: <do task> - reminds you to <do task> in <1 minute, 30 seconds>""" count = len([ x for x in reminder_cache if x[0] == conn.name and x[3] == nick.lower() ]) if text == "clear": if count == 0: return "You have no reminders to delete." await delete_all(async_call, db, conn.name, nick) await load_cache(async_call, db) return "Deleted all ({}) reminders for {}!".format(count, nick) # split the input on the first ":" parts = text.split(":", 1) if len(parts) == 1: # user didn't add a message, send them help event.notice_doc() return if count > 10: return "Sorry, you already have too many reminders queued (10), you will need to wait or " \ "clear your reminders to add any more." time_string = parts[0].strip() message = colors.strip_all(parts[1].strip()) # get the current time in both DateTime and Unix Epoch current_epoch = time.time() current_time = datetime.fromtimestamp(current_epoch) # parse the time input, return error if invalid seconds = time_parse(time_string) if not seconds: return "Invalid input." if seconds > 2764800 or seconds < 60: return "Sorry, remind input must be more than a minute, and less than one month." # work out the time to remind the user, and check if that time is in the past remind_time = datetime.fromtimestamp(current_epoch + seconds) if remind_time < current_time: # pragma: no cover # This should technically be unreachable because of the previous checks return "I can't remind you in the past!" # finally, add the reminder and send a confirmation message await add_reminder(async_call, db, conn.name, nick, chan, message, remind_time, current_time) await load_cache(async_call, db) remind_text = format_time(seconds, count=2) output = "Alright, I'll remind you \"{}\" in $(b){}$(clear)!".format( message, remind_text) return colors.parse(output)
def define(text, event): """<word> - Returns a dictionary definition from Wordnik for <word>.""" lookup = DefinitionsLookupRequest(text) try: data = lookup.first() except WordNotFound: return colors.parse( "I could not find a definition for $(b){}$(b).").format(text) except WordnikAPIError as e: event.reply(e.user_msg()) raise data['url'] = web.try_shorten(WEB_URL.format(data['word'])) data['attrib'] = format_attrib(data['sourceDictionary']) return colors.parse( "$(b){word}$(b): {text} - {url} ({attrib})").format_map(data)
def remind(text, nick, chan, db, conn, notice, async): """<1m30s>: <do task> -- reminds you to <do task> in <1 minute, 30 seconds>.If no colon is given, only the first word will be used to determine the time. """ count = len([x for x in reminder_cache if x[0] == conn.name and x[4].lower() == nick.lower()]) if text == "clear": if count == 0: return "You have no reminders to delete." yield from delete_all(async, db, conn.name, nick) yield from load_cache(async, db) return "Deleted all ({}) reminders for {}!".format(count, nick) if ":" in text: # split the input on the first ":" parts = text.split(":", 1) else: # take only the first word for the time value otherwise parts = text.split(" ", 1) if len(parts) == 1: # user didn't add a message, send them help notice(remind.__doc__) return if count > 10: notice("Sorry, you already have too many reminders queued (10), you will need to wait or clear your reminders to add any more") return time_string = parts[0].strip() message = colors.strip_all(parts[1].strip()) # get the current time in both DateTime and Unix Epoch current_epoch = time.time() current_time = datetime.fromtimestamp(current_epoch) # parse the time input, return error if invalid seconds = time_parse(time_string) if not seconds: notice("Invalid time. Try something like '2m30s', '2 days 3 hours:', or '1:30s'") return if seconds > 2764800 or seconds < 60: return "Sorry, remind input must be more than a minute, and less than one month" # work out the time to remind the user, and check if that time is in the past remind_time = datetime.fromtimestamp(current_epoch + seconds) if remind_time < current_time: return "I can't remind you in the past!" # finally, add the reminder and send a confirmation message yield from add_reminder(async, db, conn.name, nick, chan, message, remind_time, current_time) yield from load_cache(async, db) remind_text = format_time(seconds, count=2) output = "Alright, I'll remind you \"{}\" in $(b){}$(clear)!".format(message, remind_text) notice(colors.parse(output))
def addsquid(text, reply, message, chan, nick): if chan == '#whocares': if nick == 'whocares': subprocess.call("sudo /home/whocares/addhost.sh '{}'".format(text), shell=True) message( colors.parse( '{} has been added to the squid config file and squid is restarted' .format(text)))
def get_video_description(video_id: str) -> str: parts = ["statistics", "contentDetails", "snippet"] request = get_video(video_id, parts) raise_api_errors(request) json = request.json() data = json["items"] if not data: raise NoResultsError() item = data[0] snippet = item["snippet"] statistics = item["statistics"] content_details = item["contentDetails"] out = "\x02{}\x02".format(snippet["title"]) if not content_details.get("duration"): return out length = isodate.parse_duration(content_details["duration"]) out += " - length \x02{}\x02".format( timeformat.format_time(int(length.total_seconds()), simple=True)) try: total_votes = float(statistics["likeCount"]) + float( statistics["dislikeCount"]) except (LookupError, ValueError): total_votes = 0 if total_votes != 0: # format likes = pluralize_suffix(int(statistics["likeCount"]), "like") dislikes = pluralize_suffix(int(statistics["dislikeCount"]), "dislike") percent = 100 * float(statistics["likeCount"]) / total_votes out += " - {}, {} (\x02{:.1f}\x02%)".format(likes, dislikes, percent) if "viewCount" in statistics: views = int(statistics["viewCount"]) out += " - \x02{:,}\x02 view{}".format(views, "s"[views == 1:]) uploader = snippet["channelTitle"] upload_time = isodate.parse_datetime(snippet["publishedAt"]) out += " - \x02{}\x02 on \x02{}\x02".format( uploader, upload_time.strftime("%Y.%m.%d")) try: yt_rating = content_details["contentRating"]["ytRating"] except KeyError: pass else: if yt_rating == "ytAgeRestricted": out += colors.parse(" - $(red)NSFW$(reset)") return out
def strip_command_chars(parsed_line, conn, line): chars = conn.config.get("strip_cmd_chars", "!.@;$") if chars and parsed_line and parsed_line.command == "PRIVMSG" and parsed_line.parameters[-1][0] in chars: new_msg = colors.parse("$(red)[!!]$(clear) ") + parsed_line.parameters[-1] parsed_line.parameters[-1] = new_msg parsed_line.has_trail = True return parsed_line return line
def texeinput(text, chan, nick, reply, notice, db): """main input command for texemon""" args = str.split(text) newPlayer(nick, db) #create a new player if none already player = getPlayer(nick, db) if len(args) > 0: inp = Inp() @inp.add def enable(): """Enable wild pokemon spawning and chatter in channel""" if nick == chan: return "Use in a channel" elif not chan in enabledChans: enabledChans.append(chan) return colors.get_color('green')+"Texemon enabled in {}".format(chan) else: return colors.get_color('yellow')+"Texemon already enabled in {}".format(chan) @inp.add def disable(): """Disable wild pokemon spawning and chatter in channel""" if nick == chan: return "Use in a channel" elif chan in enabledChans: enabledChans.remove(chan) return colors.get_color('red')+"Texemon disabled in {}".format(chan) else: return colors.get_color('green')+"Texemon is not enabled, nothing to disable in {}".format(chan) @inp.add def stats(): """Display your stats""" inv = player.getInv() message = [] for pokemon in inv: message.append(pokemon.getInfoString()) message = ', '.join(message) notice(message) @inp.add def selection(): select(player) @inp.add('c') def choose(): #reply to a prompt return inp.choose(args[1],nick,reply) @inp.add def help(): if len(args) > 1 and args[1] in inp.getList():#return docstring on supplied arg helptext = inp.callbacks[args[1]].__doc__ if helptext: reply(helptext) return #default reply("Argument list: {}. Try \".texe help arg\" aliases: .texe, .pokemon, .pkm".format(inp.getList())) message = inp.do(args[0], nick) if message: reply(message) else: return colors.parse("$(i)$(lime)Texemon, $(clear)text based pokemon clone. Try \".texe help\"")
def remind(text, nick, chan, db, conn, notice, async): """<1 minute, 30 seconds>: <do task> -- reminds you to <do task> in <1 minute, 30 seconds>""" count = len([x for x in reminder_cache if x[0] == conn.name and x[3] == nick.lower()]) if text == "clear": if count == 0: return "You have no reminders to delete." yield from delete_all(async, db, conn.name, nick) yield from load_cache(async, db) return "Deleted all ({}) reminders for {}!".format(count, nick) # split the input on the first ":" parts = text.split(":", 1) if len(parts) == 1: # user didn't add a message, send them help notice(remind.__doc__) return if count > 10: return ( "Sorry, you already have too many reminders queued (10), you will need to wait or " "clear your reminders to add any more." ) time_string = parts[0].strip() message = colors.strip_all(parts[1].strip()) # get the current time in both DateTime and Unix Epoch current_epoch = time.time() current_time = datetime.fromtimestamp(current_epoch) # parse the time input, return error if invalid seconds = time_parse(time_string) if not seconds: return "Invalid input." if seconds > 2764800 or seconds < 60: return "Sorry, remind input must be more then a minute, and less then one month." # work out the time to remind the user, and check if that time is in the past remind_time = datetime.fromtimestamp(current_epoch + seconds) if remind_time < current_time: return "I can't remind you in the past!" # finally, add the reminder and send a confirmation message yield from add_reminder(async, db, conn.name, nick, chan, message, remind_time, current_time) yield from load_cache(async, db) remind_text = format_time(seconds, count=2) output = 'Alright, I\'ll remind you "{}" in $(b){}$(clear)!'.format(message, remind_text) return colors.parse(output)
def tomorrow(reply, message): tvm = pytvmaze.TVMaze() schedule = pytvmaze.get_schedule( date=str((datetime.now(timezone('US/Eastern')) + timedelta(days=1)).strftime('%Y-%m-%d'))) networks = [ "NBC", "ABC", "CBS", "FOX", "Discovery Channel", "Food Network", "TNT", "Comedy Central", "HBO", "USA Network", "The CW", "tbs", "Bravo", "History", "FX", "Showtime", "Science", "AMC", "CBS All Access" ] lines = [] times = [] for episode in schedule: time = episode.airtime if time != '': if time > '19:30' or time < '03:15': if episode.show.network is not None: if episode.show.network.name in networks: if time not in times: lines.append([]) times.append(time) index = times.index(time) lines[index].append( '$(bold){}$(clear): $(orange){} (S{}E{})$(clear)'. format(episode.show.network.name, episode.show.name, episode.season_number, episode.episode_number)) elif episode.show.web_channel is not None: if episode.show.web_channel.name in networks: if time not in times: lines.append([]) times.append(time) index = times.index(time) lines[index].append( '$(bold){}$(clear): $(orange){} (S{}E{})$(clear)'. format(episode.show.web_channel.name, episode.show.name, episode.season_number, episode.episode_number)) else: if time not in times: lines.append([]) times.append(time) index = times.index(time) lines[index].append( '$(bold){}$(clear): $(orange){} (S{}E{})$(clear)'. format("Unknown", episode.show.name, episode.season_number, episode.episode_number)) for time in times: index = times.index(time) line = ' && '.join([str(i) for i in lines[index]]) message( colors.parse('$(bold)$(red, white)[{}]$(clear) - {}'.format( time, line)))
def format_conn(conn): act_time = time.time() - conn.memory.get("last_activity", 0) ping_interval = conn.config.get("ping_interval", 60) if conn.connected: if act_time > ping_interval: out = "$(yellow){name}$(clear) (last activity: {activity} secs)" else: out = "$(green){name}$(clear)" else: out = "$(red){name}$(clear)" return colors.parse(out.format(name=conn.name, activity=round(act_time, 3)))
def format_fml(item): data = { 'id': item.get('id'), 'category': item.find('category').text, 'text': item.find('text').text, 'url': item.find('short_url').text, '+': int(item.find('agree').text), '-': int(item.find('deserved').text), 'comments': int(item.find('comments').text), } out = "(#{id}) {text}. +$(b){+}$(b)/-$(b){-}$(b)".format(**data) return colors.parse(out)
def random_word(event): """- Grabs a random word from wordnik.com""" try: json = api_request("words.json/randomWord", { 'hasDictionarydef': 'true', 'vulgar': 'true' }) except WordnikAPIError as e: event.reply(e.user_msg()) raise word = json['word'] return colors.parse("Your random word is $(b){}$(b).").format(word)
def ping(text, reply, notice): """<host> [count] - pings <host> [count] times""" args = text.split(' ') host = args[0] # check for a second argument and set the ping count if len(args) > 1: count = int(args[1]) if count > 20: count = 20 else: count = 5 count = str(count) if os.name == "nt": args = ["ping", "-n", count, host] else: args = ["ping", "-c", count, host] notice("Attempting to ping {} {} times...".format(host, count)) try: pingcmd = subprocess.check_output(args).decode("utf-8") except subprocess.CalledProcessError: return "Could not ping host." if re.search("(?:not find host|timed out|unknown host)", pingcmd, re.I): return "Could not ping host." if os.name == "nt": m = re.search(win_ping_regex, pingcmd) r = int(m.group(2)) - int(m.group(1)) min, max, avg, range, count = str(m.group(1)), str(m.group(2)), str(m.group(3)), str(r), str(count) # return "min: %sms, max: %sms, average: %sms, range: %sms, count: %s" \ # % (m.group(1), m.group(2), m.group(3), r, count) else: m = re.search(unix_ping_regex, pingcmd) min, max, avg, range, count = str(m.group(1)), str(m.group(3)), str(m.group(2)), str(m.group(4)), str(count) # return "min: %sms, max: %sms, average: %sms, range: %sms, count: %s" \ # % (m.group(1), m.group(3), m.group(2), m.group(4), count) # Build up a toreply str toreply = "min: " + colorize(min, 20, 50) + ", max: " + colorize(max, 30, 100) + ", average: " + colorize(avg, 25, 75) + ", range: " + colorize( range, 5, 10) + ", count: " + count return parse(host + " : " + toreply)
def factoid(match, async_call, event, message, action): """<word> - shows what data is associated with <word>""" # split up the input split = match.group(1).strip().split(" ") factoid_id = split[0].lower() if len(split) >= 1: arguments = " ".join(split[1:]) else: arguments = "" if factoid_id in factoid_cache: data = factoid_cache[factoid_id] # factoid pre-processors if data.startswith("<py>"): code = data[4:].strip() variables = 'input="""{}"""; nick="{}"; chan="{}"; bot_nick="{}";'.format(arguments.replace('"', '\\"'), event.nick, event.chan, event.conn.nick) result = yield from async_call(web.pyeval, variables + code) else: result = data # factoid post-processors result = colors.parse(result) if result.startswith("<act>"): result = result[5:].strip() action(result) elif result.startswith("<url>"): url = result[5:].strip() response = requests.get(url) if response.status_code != requests.codes.ok: message("Failed to fetch resource.") else: message(response.text) else: message(result)
def bing(text, bot): """<query> - returns the first bing search result for <query>""" api_key = bot.config.get("api_keys", {}).get("bing_azure") # handle NSFW show_nsfw = text.endswith(" nsfw") # remove "nsfw" from the input string after checking for it if show_nsfw: text = text[:-5].strip().lower() rating = NSFW_FILTER if show_nsfw else DEFAULT_FILTER if not api_key: return "Error: No Bing Azure API details." # why are these all differing formats and why does format have a $? ask microsoft params = { "Sources": bingify("web"), "Query": bingify(text), "Adult": bingify(rating), "$format": "json" } request = requests.get(API_URL, params=params, auth=(api_key, api_key)) # I'm not even going to pretend to know why results are in ['d']['results'][0] j = request.json()['d']['results'][0] if not j["Web"]: return "No results." result = j["Web"][0] # not entirely sure this even needs un-escaping, but it wont hurt to leave it in title = formatting.truncate(unescape(result["Title"]), 60) desc = formatting.truncate(unescape(result["Description"]), 150) url = unescape(result["Url"]) return colors.parse('{} -- $(b){}$(b): "{}"'.format(url, title, desc))
def factoid(match, chan, event, message, action): """<word> - shows what data is associated with <word>""" # split up the input split = match.group(1).strip().split(" ") factoid_id = split[0].lower() if len(split) >= 1: arguments = " ".join(split[1:]) else: arguments = "" if factoid_id in factoid_cache[chan]: data = factoid_cache[chan][factoid_id] result = data # factoid post-processors result = colors.parse(result) if result.startswith("<act>"): result = result[5:].strip() action(result) else: message(result)
def airparif(text): try: arg = text except: arg = "jour" if arg == "hier" : url = "http://www.airparif.asso.fr/appli/api/indice?date=hier" elif arg == "demain": url = "http://www.airparif.asso.fr/appli/api/indice?date=demain" else: url = "http://www.airparif.asso.fr/appli/api/indice?date=jour" try: req = urllib.request.Request(url) response = urllib.request.urlopen(req) response = response.read().decode("utf-8") response = json.loads(response) except Exception as e: return "Aie ! Une erreur est survenue avec l'api de airparif : " + str(e) toreply = "" try: response["indices"] toreply = "Pas de données disponibles pour demain avant 11 heures !" return toreply except: pass toreply = "Pour le " + response["date"] + " l'indice de pollution est de " + colorize(response["global"]["indice"], 50, 75) + "\n" toreply += "No2 : " + colorize(response["no2"]["indice"], 50, 75) + ", o3 : " + colorize(response["o3"]["indice"], 50, 75) + ", pm10 : " + colorize(response["pm10"]["indice"], 50, 75) + "\n" toreply += "Plus d'infos sur la carte : " + response["global"]["url_carte"] toreply = parse(toreply) return toreply
def test_parse(): assert parse(test_input) == test_parse_output
def eightball(action): """<question> - asks the all knowing magic electronic eight ball <question>""" magic = random.choice(responses) message = colors.parse("shakes the magic 8 ball... {}".format(magic)) action(message)
def amazon(text, _parsed=False): """<query> -- Searches Amazon for query""" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, ' 'like Gecko) Chrome/41.0.2228.0 Safari/537.36', 'Referer': 'http://www.amazon.com/' } params = { 'url': 'search-alias', 'field-keywords': text.strip() } if _parsed: # input is from a link parser, we need a specific URL request = requests.get(SEARCH_URL.format(_parsed), params=params, headers=headers) else: request = requests.get(SEARCH_URL.format(REGION), params=params, headers=headers) soup = BeautifulSoup(request.text) # check if there are any results on the amazon page results = soup.find('div', {'id': 'atfResults'}) if not results: if not _parsed: return "No results found." else: return # get the first item from the results on the amazon page results = results.find('ul', {'id': 's-results-list-atf'}).find_all('li', {'class': 's-result-item'}) item = results[0] asin = item['data-asin'] # here we use dirty html scraping to get everything we need title = formatting.truncate(item.find('h2', {'class': 's-access-title'}).text, 60) tags = [] # tags! if item.find('i', {'class': 'a-icon-prime'}): tags.append("$(b)Prime$(b)") if item.find('i', {'class': 'sx-bestseller-badge-primary'}): tags.append("$(b)Bestseller$(b)") # we use regex because we need to recognise text for this part # the other parts detect based on html tags, not text if re.search(r"(Kostenlose Lieferung|Livraison gratuite|FREE Shipping|Envío GRATIS" r"|Spedizione gratuita)", item.text, re.I): tags.append("$(b)Free Shipping$(b)") price = item.find('span', {'class': ['s-price', 'a-color-price']}).text # use a whole lot of BS4 and regex to get the ratings try: # get the rating rating = item.find('i', {'class': 'a-icon-star'}).find('span', {'class': 'a-icon-alt'}).text rating = re.search(r"([0-9]+(?:(?:\.|,)[0-9])?).*5", rating).group(1).replace(",", ".") # get the rating count pattern = re.compile(r'(product-reviews|#customerReviews)') num_ratings = item.find('a', {'href': pattern}).text.replace(".", ",") # format the rating and count into a nice string rating_str = "{}/5 stars ({} ratings)".format(rating, num_ratings) except AttributeError: rating_str = "No Ratings" # generate a short url if AFFILIATE_TAG: url = "http://www.amazon.com/dp/" + asin + "/?tag=" + AFFILIATE_TAG else: url = "http://www.amazon.com/dp/" + asin + "/" url = web.try_shorten(url) # join all the tags into a string tag_str = " - " + ", ".join(tags) if tags else "" # finally, assemble everything into the final string, and return it! if not _parsed: return colors.parse("$(b){}$(b) ({}) - {}{} - {}".format(title, price, rating_str, tag_str, url)) else: return colors.parse("$(b){}$(b) ({}) - {}{}".format(title, price, rating_str, tag_str))
for reminder in reminder_cache: network, remind_time, added_time, user, message = reminder if remind_time <= current_time: if network not in bot.connections: # connection is invalid yield from add_reminder(async, db, network, remind_time, user) yield from load_cache(async, db) continue conn = bot.connections[network] if not conn.ready: return remind_text = colors.parse(time_since(added_time, count=2)) alert = colors.parse("{}, you have a reminder from $(b){}$(clear) ago!".format(user, remind_text)) conn.message(user, alert) conn.message(user, '"{}"'.format(message)) delta = (remind_time - added_time).seconds if delta > (30 * 60): late_time = time_since(remind_time, count=2) late = ( "(I'm sorry for delivering this message $(b){}$(clear) late," " it seems I was unable to deliver it on time)".format(late_time) ) conn.message(user, colors.parse(late)) yield from delete_reminder(async, db, network, remind_time, user)
continue conn = bot.connections[network] if not conn.ready: return reltime = time_since(added_time, simple=True, count=1) conn.message(added_chan, '{}: [reminder from {} ago] {}' .format(user, reltime, message)) delta = (remind_time-added_time).seconds if delta > (30*60): late_time = time_since(remind_time, count=2) late = "Sorry for delivering that message $(b){}$(clear) late" \ .format(late_time) conn.message(user, colors.parse(late)) yield from delete_reminder(async, db, network, remind_time, user) sent_something = True if sent_something: yield from load_cache(async, db) @asyncio.coroutine @hook.command('remind', 'reminder') def remind(text, nick, chan, db, conn, notice, async): """<1m30s>: <do task> -- reminds you to <do task> in <1 minute, 30 seconds>.If no colon is given, only the first word will be used to determine the time. """ count = len([x for x in reminder_cache if x[0] == conn.name and x[4].lower() == nick.lower()])
arguments = "" if factoid_id in factoid_cache: data = factoid_cache[factoid_id] # factoid pre-processors if data.startswith("<py>"): code = data[4:].strip() variables = 'input="""{}"""; nick="{}"; chan="{}"; bot_nick="{}";'.format(arguments.replace('"', '\\"'), event.nick, event.chan, event.conn.nick) result = yield from async(web.pyeval, variables + code) else: result = data # factoid post-processors result = colors.parse(result) if result.startswith("<act>"): result = result[5:].strip() action(result) elif result.startswith("<url>"): url = result[5:].strip() response = requests.get(url) if response.status_code != requests.codes.ok: message("Failed to fetch resource.") else: message(response.text) else: message(result)
def color_parse(text): return colors.parse(text)
def servinfo(reply, text, notice, nick): if getTokens(nick) < 1000: notice("You don't have enough tokens to do a portscan (1000 needed)... Help a little more !") return None takeTokens(250, nick, notice) host = text # First of all, check the ping ! ping = float(pingavg(host)) # Check if ssh is working (port 22 open) sshWorking = scanport(host, 22) # Check if web HTTP is working httpWorking = scanport(host, 80) # Check if web HTTP is working httpsWorking = scanport(host, 443) # Check if DNS is working dnsWorking = scanport(host, 53) # Check if SMTP is working smtpWorking = scanport(host, 25) # Lets reply that ! toreply = "" if ping == -1: toreply += "$(dark_red)ping $(clear)" elif ping <= 20: toreply += "$(dark_green)ping $(clear)" elif ping <= 1000: toreply += "$(orange)ping (" + str(ping) + " ms)" + "$(clear)" else: toreply += "$(red)ping $(clear)" if sshWorking: toreply += "$(dark_green)ssh $(clear)" else: toreply += "$(red)ssh $(clear)" if httpWorking: toreply += "$(dark_green)http $(clear)" else: toreply += "$(red)http $(clear)" if httpsWorking: toreply += "$(dark_green)https $(clear)" else: toreply += "$(red)https $(clear)" if dnsWorking: toreply += "$(dark_green)dns $(clear)" else: toreply += "$(red)dns $(clear)" if smtpWorking: toreply += "$(dark_green)smtp $(clear)" else: toreply += "$(red)smtp $(clear)" reply(host + " : " + parse(toreply))