diff --git a/.gitignore b/.gitignore index c7db0d2de..934880f1e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,5 +55,6 @@ persist/ logs/ config.json *.db +*.mmdb *.log .idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..1df715ef3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +## Changelog + - **1.0.2** - Minor internal changes and fixes, banished minecraft_bukget and worldofwarcraft to CloudBotIRC/Plugins + - **1.0.1** - Fix history.py tracking + - **1.0.0** - Initial stable release \ No newline at end of file diff --git a/README.md b/README.md index 5e2b0d376..c9daf2cce 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CloudBot -CloudBot is a simple, fast, extendable open-source Python IRC Bot! +CloudBot is a simple, fast, expandable open-source Python IRC Bot! ## Getting CloudBot diff --git a/cloudbot/__init__.py b/cloudbot/__init__.py index 26fed6d37..8e1a6f85e 100644 --- a/cloudbot/__init__.py +++ b/cloudbot/__init__.py @@ -1,5 +1,4 @@ import sys -import warnings # check python version if sys.version_info < (3, 4, 0): @@ -11,7 +10,7 @@ import logging import os -__version__ = "1.0.0 Stable" +__version__ = "1.0.2" __all__ = ["util", "bot", "connection", "config", "permissions", "plugin", "event", "hook", "log_dir"] diff --git a/cloudbot/util/filesize.py b/cloudbot/util/filesize.py index 6c31ffdb5..2b4b108c0 100644 --- a/cloudbot/util/filesize.py +++ b/cloudbot/util/filesize.py @@ -99,7 +99,7 @@ ] -def size(bytes, system=traditional): +def size(b, system=traditional): """Human-readable file size. Using the traditional system, where a factor of 1024 is used:: @@ -150,9 +150,9 @@ def size(bytes, system=traditional): """ for factor, suffix in system: - if bytes >= factor: + if b >= factor: break - amount = int(bytes / factor) + amount = int(b / factor) if isinstance(suffix, tuple): singular, multiple = suffix if amount == 1: diff --git a/cloudbot/util/formatting.py b/cloudbot/util/formatting.py index cc242836f..7943efccb 100644 --- a/cloudbot/util/formatting.py +++ b/cloudbot/util/formatting.py @@ -168,6 +168,19 @@ def truncate(content, length=100, suffix='...'): strip_colors = strip_irc +def chunk_str(content, length=420): + """ + Chunks a string into smaller strings of given length. Returns chunks. + :rtype list + """ + def chunk(c, l): + while c: + out = (c+' ')[:l].rsplit(' ', 1)[0] + c = c[len(out):].strip() + yield out + return list(chunk(content, length)) + + def pluralize(num=0, text=''): """ Takes a number and a string, and pluralizes that string using the number and combines the results. diff --git a/cloudbot/util/timeformat.py b/cloudbot/util/timeformat.py index cb4c7f4e3..6b4c28c9a 100644 --- a/cloudbot/util/timeformat.py +++ b/cloudbot/util/timeformat.py @@ -107,7 +107,6 @@ def format_time(seconds, count=3, accuracy=6, simple=False): strings = [] i = 0 for period_name, period_seconds in periods: - print(i, period_name) if i < count: if seconds > period_seconds: period_value, seconds = divmod(seconds, period_seconds) diff --git a/plugins/amazon.py b/plugins/amazon.py index f50f40dd8..5d2a06b0c 100644 --- a/plugins/amazon.py +++ b/plugins/amazon.py @@ -11,7 +11,7 @@ @hook.command("amazon", "az") def amazon(text): - """ -- Searches Amazon for query""" + """ -- Searches Amazon for query""" headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.4 (KHTML, ' 'like Gecko) Chrome/22.0.1229.79 Safari/537.4', diff --git a/plugins/geoip.py b/plugins/geoip.py index 2ba98c98f..dc1b8ae31 100644 --- a/plugins/geoip.py +++ b/plugins/geoip.py @@ -20,7 +20,6 @@ def fetch_db(): - logger.info("fetch called") if os.path.exists(PATH): os.remove(PATH) r = requests.get(DB_URL, stream=True) @@ -34,28 +33,23 @@ def update_db(): """ Updates the DB. """ - try: - if os.path.isfile(PATH): - # check if file is over 2 weeks old - if time.time() - os.path.getmtime(PATH) > (14 * 24 * 60 * 60): - # geoip is outdated, re-download - fetch_db() - return geoip2.database.Reader(PATH) - else: - try: - return geoip2.database.Reader(PATH) - except: - # issue loading, geo - fetch_db() - return geoip2.database.Reader(PATH) - else: - # no geoip file - print("calling fetch_db") + if os.path.isfile(PATH): + # check if file is over 2 weeks old + if time.time() - os.path.getmtime(PATH) > (14 * 24 * 60 * 60): + # geoip is outdated, re-download fetch_db() - print("fetch_db finished") return geoip2.database.Reader(PATH) - except Exception as e: - print("GEOERROR" + e) + else: + try: + return geoip2.database.Reader(PATH) + except: + # issue loading, geo + fetch_db() + return geoip2.database.Reader(PATH) + else: + # no geoip file + fetch_db() + return geoip2.database.Reader(PATH) def check_db(loop): diff --git a/plugins/help.py b/plugins/help.py index 429f0fcc5..81cd5cd5d 100644 --- a/plugins/help.py +++ b/plugins/help.py @@ -3,6 +3,7 @@ import re from cloudbot import hook +from cloudbot.util import formatting @asyncio.coroutine @@ -37,13 +38,7 @@ def help_command(text, conn, bot, notice, has_permission): else: notice("Unknown command '{}'".format(searching_for)) else: - - # list of lines to send to the user - lines = [] - # current line, containing words to join with " " - current_line = [] - # current line length, to count how long the current line will be when joined with " " - current_line_length = 0 + commands = [] for plugin in sorted(set(bot.plugin_manager.commands.values()), key=attrgetter("name")): # use set to remove duplicate commands (from multiple aliases), and sorted to sort by name @@ -62,22 +57,12 @@ def help_command(text, conn, bot, notice, has_permission): # add the command to lines sent command = plugin.name - added_length = len(command) + 2 # + 2 to account for space and comma - if current_line_length + added_length > 450: - # if line limit is reached, add line to lines, and reset - lines.append(", ".join(current_line) + ",") - current_line = [] - current_line_length = 0 + commands.append(command) - current_line.append(command) - current_line_length += added_length - - if current_line: - # make sure to include the last line - lines.append(", ".join(current_line)) + # list of lines to send to the user + lines = formatting.chunk_str("Here's a list of commands you can use: " + ", ". join(commands)) - notice("Here's a list of commands you can use:") for line in lines: notice(line) notice("For detailed help, use {}help , without the brackets.".format(conn.config["command_prefix"])) diff --git a/plugins/minecraft_bukget.py b/plugins/minecraft_bukget.py deleted file mode 100644 index 863d967a6..000000000 --- a/plugins/minecraft_bukget.py +++ /dev/null @@ -1,173 +0,0 @@ -import time -import random - -import requests - -from cloudbot import hook -from cloudbot.util import web, formatting - - -# # CONSTANTS - -base_url = "http://api.bukget.org/3/" - -search_url = base_url + "search/plugin_name/like/{}" -random_url = base_url + "plugins/bukkit/?start={}&size=1" -details_url = base_url + "plugins/bukkit/{}" - - -@hook.on_start() -def load_categories(): - global categories, count_total, count_categories - categories = requests.get("http://api.bukget.org/3/categories").json() - - count_total = sum([cat["count"] for cat in categories]) - count_categories = {cat["name"].lower(): int(cat["count"]) for cat in categories} # dict comps! - - -class BukgetError(Exception): - pass - - -# DATA FUNCTIONS - -def plugin_search(term): - """ searches for a plugin with the bukget API and returns the slug """ - term = term.lower().strip() - - search_term = requests.utils.quote(term) - - try: - request = requests.get(search_url.format(search_term)) - request.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: - raise BukgetError("Error Fetching Search Page: {}".format(e)) - - try: - results = request.json() - except ValueError: - raise BukgetError("Error Parsing Search Page") - - if not results: - raise BukgetError("No Results Found") - - for result in results: - if result["slug"] == term: - return result["slug"] - - return results[0]["slug"] - - -def plugin_random(): - """ gets a random plugin from the bukget API and returns the slug """ - results = None - - while not results: - plugin_number = random.randint(1, count_total) - print("trying {}".format(plugin_number)) - try: - request = requests.get(random_url.format(plugin_number)) - request.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: - raise BukgetError("Error Fetching Search Page: {}".format(e)) - - try: - results = request.json() - except ValueError: - raise BukgetError("Error Parsing Search Page") - - return results[0]["slug"] - - -def plugin_details(slug): - """ takes a plugin slug and returns details from the bukget API """ - slug = slug.lower().strip() - - try: - request = requests.get(details_url.format(slug)) - request.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: - raise BukgetError("Error Fetching Details: {}".format(e)) - - try: - details = request.json() - except ValueError: - raise BukgetError("Error Parsing Details") - - return details - - -# OTHER FUNCTIONS - -def format_output(data): - """ takes plugin data and returns two strings representing information about that plugin """ - name = data["plugin_name"] - description = formatting.truncate(data['description'], 30) - url = data['website'] - if data['authors']: - authors = data['authors'][0] - authors = authors[0] + "\u200b" + authors[1:] - else: - authors = "Unknown" - - stage = data['stage'] - - current_version = data['versions'][0] - - last_update = time.strftime('%d %B %Y %H:%M', - time.gmtime(current_version['date'])) - version_number = data['versions'][0]['version'] - - bukkit_versions = ", ".join(current_version['game_versions']) - link = web.try_shorten(current_version['link']) - - if description: - line_a = "\x02{}\x02, by \x02{}\x02 - {} - ({}) - {}".format(name, authors, description, stage, url) - else: - line_a = "\x02{}\x02, by \x02{}\x02 ({}) - {}".format(name, authors, stage, url) - - line_b = "Last release: \x02v{}\x02 for \x02{}\x02 at {} - {}".format(version_number, bukkit_versions, - last_update, link) - - return line_a, line_b - - -# HOOK FUNCTIONS - -@hook.command("bukget", "plugin") -def bukget(text): - """ - gets details on a plugin from dev.bukkit.org""" - # get the plugin slug using search - try: - slug = plugin_search(text) - except BukgetError as e: - return e - - # get the plugin info using the slug - try: - data = plugin_details(slug) - except BukgetError as e: - return e - - # format the final message and send it to IRC - return format_output(data) - - -@hook.command(autohelp=None) -def randomplugin(): - """- gets details on a random plugin from dev.bukkit.org""" - # get a random plugin slug - try: - slug = plugin_random() - except BukgetError as e: - return e - - # get the plugin info using the slug - try: - data = plugin_details(slug) - except BukgetError as e: - return e - - # format the final message and send it to IRC - return format_output(data) - diff --git a/plugins/password.py b/plugins/password.py index 928300633..4bc4aaff2 100644 --- a/plugins/password.py +++ b/plugins/password.py @@ -30,6 +30,9 @@ def password(text, notice): except ValueError: length = 12 + if length > 50: + notice("Maximum length is 50 characters.") + # add alpha characters if "alpha" in text or "letter" in text: okay += list(string.ascii_lowercase) diff --git a/plugins/quote.py b/plugins/quote.py index d75813d4e..db470eb92 100644 --- a/plugins/quote.py +++ b/plugins/quote.py @@ -159,12 +159,12 @@ def quote(text, nick, chan, db, notice): notice(add_quote(db, chan, quoted_nick, nick, msg)) return elif retrieve: - select, num = retrieve.groups() - by_chan = True if select.startswith('#') else False + selected, num = retrieve.groups() + by_chan = True if selected.startswith('#') else False if by_chan: - return get_quote_by_chan(db, select, num) + return get_quote_by_chan(db, selected, num) else: - return get_quote_by_nick(db, select, num) + return get_quote_by_nick(db, selected, num) elif retrieve_chan: chan, nick, num = retrieve_chan.groups() return get_quote_by_nick_chan(db, chan, nick, num) diff --git a/plugins/rottentomatoes.py b/plugins/rottentomatoes.py index 28dc4e08c..3919c5c44 100644 --- a/plugins/rottentomatoes.py +++ b/plugins/rottentomatoes.py @@ -9,7 +9,7 @@ @hook.command('rottentomatoes', 'rt') -def rottentomatoes(text, bot): +def rotten_tomatoes(text, bot): """rt -- gets ratings for <title> from Rotten Tomatoes""" api_key = bot.config.get("api_keys", {}).get("rottentomatoes", None) if not api_key: diff --git a/plugins/speedtest.py b/plugins/speedtest.py index b95b9db66..31a060aa6 100644 --- a/plugins/speedtest.py +++ b/plugins/speedtest.py @@ -22,8 +22,6 @@ def speedtest_url(match): upload = data.xpath('//div[@class="share-speed share-upload"]/p')[0].text_content().strip() ping = data.xpath('//div[@class="share-data share-ping"]/p')[0].text_content().strip() - stars = data.xpath('//div[contains(@class, "share-stars")]')[0].text_content().strip() - isp = data.xpath('//div[@class="share-data share-isp"]/p')[0].text_content().strip().title() return "\x02{}\x02 - Download: \x02{}\x02, Upload: \x02{}\x02, Ping: \x02{}\x02".format(isp, download, upload, ping) diff --git a/plugins/steam_user.py b/plugins/steam_user.py index a74f6627c..ceea940b7 100644 --- a/plugins/steam_user.py +++ b/plugins/steam_user.py @@ -21,8 +21,7 @@ def convert_id32(id_64): :type id_64: int :return: str """ - out = [] - out.append("STEAM_0:") + out = ["STEAM_0:"] final = id_64 - ID_BASE if final % 2 == 0: out.append("0:") diff --git a/plugins/system.py b/plugins/system.py index d47a8f0fe..07c2f3be3 100644 --- a/plugins/system.py +++ b/plugins/system.py @@ -12,9 +12,7 @@ @hook.command(autohelp=False) def about(text, conn): - """-- Gives information about CloudBot. Use .about license for licensing information - :type event: cloudbot.event.Event - """ + """-- Gives information about CloudBot. Use .about license for licensing information""" if text.lower() in ("license", "gpl", "source"): return "CloudBot Refresh is released under the GPL v3 license, get the source code " \ "at https://github.com/CloudBotIRC/CloudBot/" diff --git a/plugins/tvdb.py b/plugins/tvdb.py index 1a6a47a8b..3371c91b2 100644 --- a/plugins/tvdb.py +++ b/plugins/tvdb.py @@ -32,7 +32,7 @@ def get_episodes_for_series(series_name, api_key): try: _request = requests.get(base_url + '%s/series/%s/all/en.xml' % (api_key, series_id)) _request.raise_for_status() - except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e: + except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError): res["error"] = "error contacting thetvdb.com" return res diff --git a/plugins/twitch.py b/plugins/twitch.py index ca77f29ff..f0d357544 100644 --- a/plugins/twitch.py +++ b/plugins/twitch.py @@ -1,41 +1,70 @@ import re import html - + from cloudbot import hook from cloudbot.util import http - + + twitch_re = re.compile(r'(.*:)//(twitch.tv|www.twitch.tv)(:[0-9]+)?(.*)', re.I) multitwitch_re = re.compile(r'(.*:)//(www.multitwitch.tv|multitwitch.tv)/(.*)', re.I) - - -def test(s): + + +def test_name(s): valid = set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_/') return set(s) <= valid - - -def truncate(msg): - nmsg = msg.split(" ") - out = None - x = 0 - for i in nmsg: - if x <= 7: - if out: - out = out + " " + nmsg[x] - else: - out = nmsg[x] - x += 1 - if x <= 7: - return out + + +def twitch_lookup(location): + locsplit = location.split("/") + if len(locsplit) > 1 and len(locsplit) == 3: + channel = locsplit[0] + _type = locsplit[1] # should be b or c + _id = locsplit[2] + else: + channel = locsplit[0] + _type = None + _id = None + fmt = "{}: {} playing {} ({})" # Title: nickname playing Game (x views) + if _type and _id: + if _type == "b": # I haven't found an API to retrieve broadcast info + soup = http.get_soup("http://twitch.tv/" + location) + title = soup.find('span', {'class': 'real_title js-title'}).text + playing = soup.find('a', {'class': 'game js-game'}).text + views = soup.find('span', {'id': 'views-count'}).text + " view" + views = views + "s" if not views[0:2] == "1 " else views + return html.unescape(fmt.format(title, channel, playing, views)) + elif _type == "c": + data = http.get_json("https://api.twitch.tv/kraken/videos/" + _type + _id) + title = data['title'] + playing = data['game'] + views = str(data['views']) + " view" + views = views + "s" if not views[0:2] == "1 " else views + return html.unescape(fmt.format(title, channel, playing, views)) else: - return out + "..." - - + data = http.get_json("https://api.twitch.tv/kraken/streams?channel=" + channel) + if data["streams"]: + title = data["streams"][0]["channel"]["status"] + playing = data["streams"][0]["game"] + v = data["streams"][0]["viewers"] + viewers = "\x033\x02Online now!\x02\x0f " + str(v) + " viewer" + ("s" if v != 1 else "") + return html.unescape(fmt.format(title, channel, playing, viewers)) + else: + try: + data = http.get_json("https://api.twitch.tv/kraken/channels/" + channel) + except: + return "Unable to get channel data. Maybe channel is on justin.tv instead of twitch.tv?" + title = data['status'] + playing = data['game'] + viewers = "\x034\x02Offline\x02\x0f" + return html.unescape(fmt.format(title, channel, playing, viewers)) + + @hook.regex(multitwitch_re) def multitwitch_url(match): usernames = match.group(3).split("/") out = "" for i in usernames: - if not test(i): + if not test_name(i): print("Not a valid username") return None if out == "": @@ -43,61 +72,23 @@ def multitwitch_url(match): else: out = out + " \x02|\x02 " + twitch_lookup(i) return out - - + + @hook.regex(twitch_re) def twitch_url(match): bit = match.group(4).split("#")[0] location = "/".join(bit.split("/")[1:]) - if not test(location): + if not test_name(location): print("Not a valid username") return None return twitch_lookup(location) - - -@hook.command('twitchviewers') -@hook.command() -def twviewers(text): + + +@hook.command('twitch', 'twviewers') +def twitch(text): text = text.split("/")[-1] - if test(text): + if test_name(text): location = text else: return "Not a valid channel name." return twitch_lookup(location).split("(")[-1].split(")")[0].replace("Online now! ", "") - - -def twitch_lookup(location): - locsplit = location.split("/") - if len(locsplit) > 1 and len(locsplit) == 3: - channel = locsplit[0] - type = locsplit[1] # should be b or c - id = locsplit[2] - else: - channel = locsplit[0] - type = None - id = None - fmt = "{}: {} playing {} ({})" # Title: nickname playing Game (x views) - if type and id: - if type == "b": # I haven't found an API to retrieve broadcast info - soup = http.get_soup("http://twitch.tv/" + location) - title = soup.find('span', {'class': 'real_title js-title'}).text - playing = soup.find('a', {'class': 'game js-game'}).text - views = soup.find('span', {'id': 'views-count'}).text + " view" - views = views + "s" if not views[0:2] == "1 " else views - return html.unescape(fmt.format(title, channel, playing, views)) - elif type == "c": - data = http.get_json("https://api.twitch.tv/kraken/videos/" + type + id) - title = data['title'] - playing = data['game'] - views = str(data['views']) + " view" - views = views + "s" if not views[0:2] == "1 " else views - return html.unescape(fmt.format(title, channel, playing, views)) - else: - data = http.get_json("https://api.twitch.tv/kraken/channels/" + channel) - if data and len(data) >= 1: - title = data['status'] - playing = data['game'] - viewers = "\x034\x02Offline\x02\x0f" - return html.unescape(fmt.format(title, channel, playing, viewers)) - else: - return diff --git a/plugins/whois.py b/plugins/whois.py index 0609a9063..b051207f3 100644 --- a/plugins/whois.py +++ b/plugins/whois.py @@ -1,7 +1,5 @@ import pythonwhois -from pprint import pprint - from cloudbot import hook diff --git a/plugins/worldofwarcraft.py b/plugins/worldofwarcraft.py deleted file mode 100644 index b86ebb0fc..000000000 --- a/plugins/worldofwarcraft.py +++ /dev/null @@ -1,166 +0,0 @@ -""" -wow.py: -Written by Zarthus <zarthus@zarth.us> May 30, 2014. -Gets data from the World of Warcraft Armoury API - -Commands: -armoury, armory: Request data from the armoury API and format it into something human readable. -""" - -import re - -import requests - -from cloudbot import hook -from cloudbot.util import web - - -def wow_armoury_data(link): - """Sends the API request, and returns the data accordingly (in json if raw, nicely formatted if not).""" - try: - data = requests.get(link) - except Exception as e: - return 'Unable to fetch information for {}. Does the realm or character exist? ({})'.format(link, str(e)) - - return wow_armoury_format(data, link) - - -def wow_armoury_format(data, link): - """Format armoury data into a human readable string""" - - if data.status_code != 200 and data.status_code != 404: - # The page returns 404 if the character or realm is not found. - try: - data.raise_for_status() - except Exception as e: - return 'An error occurred while trying to fetch the data. ({})'.format(str(e)) - - data = data.json() - - if len(data) == 0: - return 'Could not find any results.' - - if 'reason' in data: - # Something went wrong (i.e. realm does not exist, character does not exist, or page not found). - return data['reason'] - - if 'name' in data: - nice_url = link.replace('/api/wow/', '/wow/en/') + '/simple' - - try: - return '{} is a level \x0307{}\x0F {} {} on {} with \x0307{}\x0F achievement points and \x0307{}' \ - '\x0F honourable kills. Armoury Profile: {}' \ - .format(data['name'], data['level'], wow_get_gender(data['gender']), wow_get_class(data['class'], True), - data['realm'], data['achievementPoints'], data['totalHonorableKills'], web.shorten(nice_url)) - except Exception as e: - return 'Unable to fetch information for {}. Does the realm or ' \ - 'character exist? ({})'.format(nice_url, str(e)) - - return 'An unexpected error occurred.' - - -def wow_get_gender(gender_id): - """Formats a gender ID to a readable gender name""" - gender = 'unknown' - - if gender_id == 0: - gender = 'male' - elif gender_id == 1: - gender = 'female' - - return gender - - -def wow_get_class(class_id, colours=False): - """Formats a class ID to a readable name, data from http://eu.battle.net/api/wow/data/character/classes""" - if colours: - # Format their colours according to class colours. - class_ids = { - 1: "\x0305Warrior\x0F", 2: "\x0313Paladin\x0F", 3: "\x0303Hunter\x0F", 4: "\x0308Rogue\x0F", - 5: "Priest", 6: "\x0304Death Knight\x0F", 7: "\x0310Shaman\x0F", 8: "\x0311Mage\x0F", - 9: "\x0306Warlock\x0F", 10: "\x0309Monk\x0F", 11: "\x0307Druid\x0F" - } - else: - class_ids = { - 1: "Warrior", 2: "Paladin", 3: "Hunter", 4: "Rogue", 5: "Priest", - 6: "Death Knight", 7: "Shaman", 8: "Mage", 9: "Warlock", 10: "Monk", - 11: "Druid" - } - - if class_id in class_ids: - return class_ids[class_id] - else: - return 'unknown' - - -def wow_get_race(race_id): - """Formats a race ID to a readable race name, data from http://eu.battle.net/api/wow/data/character/races""" - race_ids = { - 1: "Human", 2: "Orc", 3: "Dwarf", 4: "Night Elf", 5: "Undead", 6: "Tauren", 7: "Gnome", - 8: "Troll", 9: "Goblin", 10: "Blood Elf", 11: "Draenei", 22: "Worgen", - 24: "Pandaren (neutral)", 25: "Pandaren (alliance)", 26: "Pandaren (horde)" - } - - if race_id in race_ids: - return race_ids[race_id] - else: - return 'unknown' - - -def wow_region_shortname(region): - """Returns a short region name, which functions as battle.net their subdomain (i.e. eu.battle.net)""" - valid_regions = { - 'eu': 'eu', 'europe': 'eu', - 'us': 'us', - 'sea': 'sea', 'asia': 'sea', - 'kr': 'kr', 'korea': 'kr', - 'tw': 'tw', 'taiwan': 'tw' - } - - if region in valid_regions: - return valid_regions[region] - else: - return False - - -@hook.command('armory', 'armoury') -def armoury(text): - """armoury [realm] [character name] [region = EU] - Look up character and returns API data.""" - - # Splits the input, builds the API url, and returns the formatted data to user. - split_input = text.lower().split() - - if len(split_input) < 2: - return 'armoury [realm] [character name] [region = EU] - Look up character and returns API data.' - - realm = split_input[0].replace('_', '-') - char_name = split_input[1] - - # Sets the default region to EU if none specified. - if len(split_input) < 3: - region = 'eu' - else: - region = split_input[2] - - if not re.match(r"^[a-z]{1,3}$", region): - return 'The region specified is not a valid region. Valid regions: eu, us, sea, kr, tw.' - - if re.match(r"^[^\d]$", char_name) or len(char_name) > 18: - # May not contain digits, repeat the same letter three times, or contain non-word characters. - # Special characters are permitted, such as áéíóßø. - return 'The character name is not a valid name. Character names can only contain letters, special characters,' \ - ' and be 18 characters long.' - - if not re.match(r"^[a-z' _-]{3,32}$", realm): - # Realm names can have spaces in them, use dashes for this. - return 'The realm name is not a valid name. Realm names can only contain letters, dashes, and apostrophes, up' \ - ' to 32 characters' - - region_short = wow_region_shortname(region) - - if not region_short: - return 'The region \'{}\' does not exist.'.format(region) - - link = "http://{}.battle.net/api/wow/character/{}/{}".format(region, realm, char_name) - - return wow_armoury_data(link) diff --git a/plugins/youtube.py b/plugins/youtube.py index 6f05c4e82..56f44ef9e 100644 --- a/plugins/youtube.py +++ b/plugins/youtube.py @@ -1,8 +1,7 @@ import re import time -import isodate -import bs4 +import isodate import requests from cloudbot import hook @@ -17,7 +16,7 @@ search_api_url = base_url + 'search?part=id&maxResults=1' playlist_api_url = base_url + 'playlists?part=snippet%2CcontentDetails%2Cstatus' video_url = "http://youtu.be/%s" -err_noapi = "The YouTube API is off in the Google Developers Console." +err_no_api = "The YouTube API is off in the Google Developers Console." def get_video_description(video_id, key): @@ -25,7 +24,7 @@ def get_video_description(video_id, key): if json.get('error'): if json['error']['code'] == 403: - return err_noapi + return err_no_api else: return @@ -41,14 +40,14 @@ def get_video_description(video_id, key): length = isodate.parse_duration(content_details['duration']) out += ' - length \x02{}\x02'.format(timeformat.format_time(int(length.total_seconds()), simple=True)) - totalvotes = float(statistics['likeCount']) + float(statistics['dislikeCount']) + total_votes = float(statistics['likeCount']) + float(statistics['dislikeCount']) - if totalvotes != 0: + if total_votes != 0: # format likes = pluralize(int(statistics['likeCount']), "like") dislikes = pluralize(int(statistics['dislikeCount']), "dislike") - percent = 100 * float(statistics['likeCount']) / totalvotes + percent = 100 * float(statistics['likeCount']) / total_votes out += ' - {}, {} (\x02{:.1f}\x02%)'.format(likes, dislikes, percent) @@ -75,7 +74,7 @@ def load_key(bot): @hook.regex(youtube_re) -def youtube_url(match, bot): +def youtube_url(match): return get_video_description(match.group(1), dev_key) @@ -89,7 +88,7 @@ def youtube(text): if json.get('error'): if json['error']['code'] == 403: - return err_noapi + return err_no_api else: return 'Error performing search.' @@ -111,7 +110,7 @@ def youtime(text): if json.get('error'): if json['error']['code'] == 403: - return err_noapi + return err_no_api else: return 'Error performing search.' @@ -154,7 +153,7 @@ def ytplaylist_url(match): if json.get('error'): if json['error']['code'] == 403: - return err_noapi + return err_no_api else: return 'Error looking up playlist.'