Merge branch 'develop' into refresh

Conflicts:
	core/irc.py
	plugins/correction.py
	plugins/history.py
	requirements.txt
This commit is contained in:
Luke Rogers 2014-03-06 10:56:05 +13:00
commit 9f029c8ceb
17 changed files with 463 additions and 210 deletions

View file

@ -213,6 +213,7 @@ class IRC(object):
self.logger = logger self.logger = logger
self.nick = nick self.nick = nick
self.vars = {} self.vars = {}
self.history = {}
self.parsed_queue = Queue.Queue() # responses from the server are placed here self.parsed_queue = Queue.Queue() # responses from the server are placed here
# format: [rawline, prefix, command, params, # format: [rawline, prefix, command, params,

View file

@ -193,11 +193,11 @@ def say(inp, conn=None, chan=None):
the command was used in.""" the command was used in."""
inp = inp.split(" ") inp = inp.split(" ")
if inp[0][0] == "#": if inp[0][0] == "#":
message = " ".join(inp[1:]) message = u" ".join(inp[1:])
out = "PRIVMSG {} :{}".format(inp[0], message) out = u"PRIVMSG {} :{}".format(inp[0], message)
else: else:
message = " ".join(inp[0:]) message = u" ".join(inp[0:])
out = "PRIVMSG {} :{}".format(chan, message) out = u"PRIVMSG {} :{}".format(chan, message)
conn.send(out) conn.send(out)
@ -213,11 +213,11 @@ def me(inp, conn=None, chan=None):
for x in inp[1:]: for x in inp[1:]:
message = message + x + " " message = message + x + " "
message = message[:-1] message = message[:-1]
out = "PRIVMSG {} :\x01ACTION {}\x01".format(inp[0], message) out = u"PRIVMSG {} :\x01ACTION {}\x01".format(inp[0], message)
else: else:
message = "" message = ""
for x in inp[0:]: for x in inp[0:]:
message = message + x + " " message = message + x + " "
message = message[:-1] message = message[:-1]
out = "PRIVMSG {} :\x01ACTION {}\x01".format(chan, message) out = u"PRIVMSG {} :\x01ACTION {}\x01".format(chan, message)
conn.send(out) conn.send(out)

View file

@ -1,6 +1,8 @@
from util import hook from util import hook
import re
<<<<<<< HEAD
@hook.regex(r'^(s|S)/.*/.*/\S*$') @hook.regex(r'^(s|S)/.*/.*/\S*$')
def correction(inp, message=None, input=None, notice=None, db=None): def correction(inp, message=None, input=None, notice=None, db=None):
splitinput = input.msg.split("/") splitinput = input.msg.split("/")
@ -24,8 +26,38 @@ def correction(inp, message=None, input=None, notice=None, db=None):
message(u"Correction, <{}> {}".format(nick, msg.replace(find, "\x02" + replace + "\x02"))) message(u"Correction, <{}> {}".format(nick, msg.replace(find, "\x02" + replace + "\x02")))
else: else:
notice(u"{} can't be found in your last message".format(find)) notice(u"{} can't be found in your last message".format(find))
=======
CORRECTION_RE = r'^(s|S)/.*/.*/?\S*$'
@hook.regex(CORRECTION_RE)
def correction(match, input=None, conn=None, message=None):
split = input.msg.split("/")
if len(split) == 4:
nick = split[3].lower()
>>>>>>> develop
else: else:
if nick == input.nick: nick = None
notice(u"I haven't seen you say anything here yet")
find = split[1]
replace = split[2]
for item in conn.history[input.chan].__reversed__():
name, timestamp, msg = item
if msg.startswith("s/"):
# don't correct corrections, it gets really confusing
continue
if nick:
if nick != name.lower():
continue
if find in msg:
if "\x01ACTION" in msg:
msg = msg.replace("\x01ACTION ", "/me ").replace("\x01", "")
message(u"Correction, <{}> {}".format(name, msg.replace(find, "\x02" + replace + "\x02")))
return
else: else:
notice(u"I haven't seen {} say anything here yet".format(nick)) continue
return u"Did not find {} in any recent messages.".format(find)

View file

@ -8,15 +8,6 @@ exchanges = {
"func": lambda data: u"Blockchain // Buy: \x0307${:,.2f}\x0f -" "func": lambda data: u"Blockchain // Buy: \x0307${:,.2f}\x0f -"
u" Sell: \x0307${:,.2f}\x0f".format(data["USD"]["buy"], data["USD"]["sell"]) u" Sell: \x0307${:,.2f}\x0f".format(data["USD"]["buy"], data["USD"]["sell"])
}, },
"mtgox": {
"api_url": "https://mtgox.com/api/1/BTCUSD/ticker",
"func": lambda data: u"MtGox // Current: \x0307{}\x0f - High: \x0307{}\x0f - Low: \x0307{}\x0f"
u" - Best Ask: \x0307{}\x0f - Volume: {}".format(data['return']['last']['display'],
data['return']['high']['display'],
data['return']['low']['display'],
data['return']['buy']['display'],
data['return']['vol']['display'])
},
"coinbase": { "coinbase": {
"api_url": "https://coinbase.com/api/v1/prices/spot_rate", "api_url": "https://coinbase.com/api/v1/prices/spot_rate",
"func": lambda data: u"Coinbase // Current: \x0307${:,.2f}\x0f".format(float(data['amount'])) "func": lambda data: u"Coinbase // Current: \x0307${:,.2f}\x0f".format(float(data['amount']))

View file

@ -1,29 +1,24 @@
"""seen.py: written by sklnd in about two beers July 2009""" from collections import deque
from util import hook, timesince
import time import time
import re import re
from util import hook, timesince db_ready = []
db_ready = False def db_init(db, conn_name):
"""check to see that our db has the the seen table (connection name is for caching the result per connection)"""
def db_init(db):
"""check to see that our db has the the seen table and return a connection."""
global db_ready global db_ready
if not db_ready: if db_ready.count(conn_name) < 1:
db.execute("create table if not exists seen_user(name, time, quote, chan, host, " db.execute("create table if not exists seen_user(name, time, quote, chan, host, "
"primary key(name, chan))") "primary key(name, chan))")
db.commit() db.commit()
db_ready = True db_ready.append(conn_name)
@hook.singlethread def track_seen(input, message_time, db, conn):
@hook.event('PRIVMSG', ignorebots=False) """ Tracks messages for the .seen command """
def seen_sieve(paraml, input=None, db=None): db_init(db, conn)
if not db_ready:
db_init(db)
# keep private messages private # keep private messages private
if input.chan[:1] == "#" and not re.findall('^s/.*/.*/$', input.msg.lower()): if input.chan[:1] == "#" and not re.findall('^s/.*/.*/$', input.msg.lower()):
db.execute("insert or replace into seen_user(name, time, quote, chan, host)" db.execute("insert or replace into seen_user(name, time, quote, chan, host)"
@ -35,8 +30,38 @@ def seen_sieve(paraml, input=None, db=None):
db.commit() db.commit()
def track_history(input, message_time, conn):
try:
history = conn.history[input.chan]
except KeyError:
conn.history[input.chan] = deque(maxlen=100)
history = conn.history[input.chan]
data = (input.nick, message_time, input.msg)
history.append(data)
@hook.singlethread
@hook.event('PRIVMSG', ignorebots=False)
def chat_tracker(paraml, input=None, db=None, conn=None):
message_time = time.time()
track_seen(input, message_time, db, conn)
track_history(input, message_time, conn)
@hook.command(autohelp=False)
def resethistory(inp, input=None, conn=None):
"""resethistory - Resets chat history for the current channel"""
try:
conn.history[input.chan].clear()
return "Reset chat history for current channel."
except KeyError:
# wat
return "There is no history for this channel."
@hook.command @hook.command
def seen(inp, nick='', chan='', db=None, input=None): def seen(inp, nick='', chan='', db=None, input=None, conn=None):
"""seen <nick> <channel> -- Tell when a nickname was last in active in one of this bot's channels.""" """seen <nick> <channel> -- Tell when a nickname was last in active in one of this bot's channels."""
if input.conn.nick.lower() == inp.lower(): if input.conn.nick.lower() == inp.lower():
@ -48,8 +73,7 @@ def seen(inp, nick='', chan='', db=None, input=None):
if not re.match("^[A-Za-z0-9_|.\-\]\[]*$", inp.lower()): if not re.match("^[A-Za-z0-9_|.\-\]\[]*$", inp.lower()):
return "I can't look up that name, its impossible to use!" return "I can't look up that name, its impossible to use!"
if not db_ready: db_init(db, conn.name)
db_init(db)
last_seen = db.execute("select name, time, quote from seen_user where name" last_seen = db.execute("select name, time, quote from seen_user where name"
" like :name and chan = :chan", {'name': inp, 'chan': chan}).fetchone() " like :name and chan = :chan", {'name': inp, 'chan': chan}).fetchone()

View file

@ -1,31 +1,46 @@
# TODO: Rewrite this whole mess
import socket import socket
import struct import struct
import json import json
import traceback
from util import hook from util import hook
try: try:
import DNS import DNS
# Please remember to install the dependency 'pydns' has_dns = True
pydns_installed = True
except ImportError: except ImportError:
pydns_installed = False has_dns = False
mccolors = [u"\x0300,\xa7f", u"\x0301,\xa70", u"\x0302,\xa71", u"\x0303,\xa72", u"\x0304,\xa7c", u"\x0305,\xa74", mc_colors = [(u'\xa7f', u'\x0300'), (u'\xa70', u'\x0301'), (u'\xa71', u'\x0302'), (u'\xa72', u'\x0303'),
u"\x0306,\xa75", u"\x0307,\xa76", u"\x0308,\xa7e", u"\x0309,\xa7a", u"\x0310,\xa73", u"\x0311,\xa7b", (u'\xa7c', u'\x0304'), (u'\xa74', u'\x0305'), (u'\xa75', u'\x0306'), (u'\xa76', u'\x0307'),
u"\x0312,\xa71", u"\x0313,\xa7d", u"\x0314,\xa78", u"\x0315,\xa77", u"\x02,\xa7l", u"\x0310,\xa79", (u'\xa7e', u'\x0308'), (u'\xa7a', u'\x0309'), (u'\xa73', u'\x0310'), (u'\xa7b', u'\x0311'),
u"\x09,\xa7o", u"\x13,\xa7m", u"\x0f,\xa7r", u"\x15,\xa7n"] (u'\xa71', u'\x0312'), (u'\xa7d', u'\x0313'), (u'\xa78', u'\x0314'), (u'\xa77', u'\x0315'),
(u'\xa7l', u'\x02'), (u'\xa79', u'\x0310'), (u'\xa7o', u'\t'), (u'\xa7m', u'\x13'),
(u'\xa7r', u'\x0f'), (u'\xa7n', u'\x15')]
def mc_color_format(motd): ## EXCEPTIONS
for colorcombo in mccolors:
colorarray = colorcombo.split(",")
motd = motd.replace(colorarray[1], colorarray[0]) class PingError(Exception):
motd = motd.replace(u"\xa7k", "") def __init__(self, text):
return motd self.text = text
def __str__(self):
return self.text
class ParseError(Exception):
def __init__(self, text):
self.text = text
def __str__(self):
return self.text
## MISC
def unpack_varint(s): def unpack_varint(s):
@ -38,20 +53,24 @@ def unpack_varint(s):
if not b & 0x80: if not b & 0x80:
return d return d
pack_data = lambda d: struct.pack('>b', len(d)) + d
pack_port = lambda i: struct.pack('>H', i)
def pack_data(d): ## DATA FUNCTIONS
return struct.pack('>b', len(d)) + d
def pack_port(i):
return struct.pack('>H', i)
def mcping_modern(host, port): def mcping_modern(host, port):
""" pings a server using the modern (1.7+) protocol and returns formatted output """ """ pings a server using the modern (1.7+) protocol and returns data """
try:
# connect to the server # connect to the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((host, port)) s.connect((host, port))
except socket.gaierror:
raise PingError("Invalid hostname")
except socket.timeout:
raise PingError("Request timed out")
# send handshake + status request # send handshake + status request
s.send(pack_data("\x00\x00" + pack_data(host.encode('utf8')) + pack_port(port) + "\x01")) s.send(pack_data("\x00\x00" + pack_data(host.encode('utf8')) + pack_port(port) + "\x01"))
@ -63,7 +82,7 @@ def mcping_modern(host, port):
l = unpack_varint(s) # String length l = unpack_varint(s) # String length
if not l > 1: if not l > 1:
raise Exception raise PingError("Invalid response")
d = "" d = ""
while len(d) < l: while len(d) < l:
@ -71,48 +90,79 @@ def mcping_modern(host, port):
# Close our socket # Close our socket
s.close() s.close()
except socket.error:
raise PingError("Socket Error")
# Load json and return # Load json and return
data = json.loads(d.decode('utf8')) data = json.loads(d.decode('utf8'))
try: try:
version = data["version"]["name"] version = data["version"]["name"]
if data["description"].get("text", None): try:
desc = u" ".join(data["description"]["text"].split()) desc = u" ".join(data["description"]["text"].split())
else: except TypeError:
desc = u" ".join(data["description"].split()) desc = u" ".join(data["description"].split())
max_players = data["players"]["max"] max_players = data["players"]["max"]
online = data["players"]["online"] online = data["players"]["online"]
except Exception as e: except Exception as e:
return "Invalid data: {}; error: {}".format(data, e) # TODO: except Exception is bad
return mc_color_format(u"{}\x0f - {}\x0f - {}/{} players.".format(desc, version, online, traceback.print_exc(e)
max_players)).replace("\n", u"\x0f - ") raise PingError("Unknown Error: {}".format(e))
output = {
"motd": format_colors(desc),
"motd_raw": desc,
"version": version,
"players": online,
"players_max": max_players
}
return output
def mcping_legacy(host, port): def mcping_legacy(host, port):
""" pings a server using the legacy (1.6 and older) protocol and returns formatted output """ """ pings a server using the legacy (1.6 and older) protocol and returns data """
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port)) sock.connect((host, port))
sock.send('\xfe\x01') sock.send('\xfe\x01')
response = sock.recv(1) response = sock.recv(1)
print response except socket.gaierror:
raise PingError("Invalid hostname")
except socket.timeout:
raise PingError("Request timed out")
if response[0] != '\xff': if response[0] != '\xff':
return "Server gave invalid response: " + repr(response) raise PingError("Invalid response")
length = struct.unpack('!h', sock.recv(2))[0] length = struct.unpack('!h', sock.recv(2))[0]
values = sock.recv(length * 2).decode('utf-16be') values = sock.recv(length * 2).decode('utf-16be')
data = values.split(u'\x00') # try to decode data using new format data = values.split(u'\x00') # try to decode data using new format
if len(data) == 1: if len(data) == 1:
# failed to decode data, server is using old format # failed to decode data, server is using old format
data = values.split(u'\xa7') data = values.split(u'\xa7')
message = u"{} - {}/{} players".format(mc_color_format(data[0]), data[1], data[2]) output = {
"motd": format_colors(" ".join(data[0].split())),
"motd_raw": data[0],
"version": None,
"players": data[1],
"players_max": data[2]
}
else: else:
# decoded data, server is using new format # decoded data, server is using new format
message = u"{} \x0f- {} - {}/{} players".format(mc_color_format(data[3]), output = {
mc_color_format(data[2]), data[4], data[5]) "motd": format_colors(" ".join(data[3].split())),
"motd_raw": data[3],
"version": data[2],
"players": data[4],
"players_max": data[5]
}
sock.close() sock.close()
return message return output
def get_srv_data(domain): ## FORMATTING/PARSING FUNCTIONS
def check_srv(domain):
""" takes a domain and finds minecraft SRV records """ """ takes a domain and finds minecraft SRV records """
DNS.DiscoverNameServers() DNS.DiscoverNameServers()
srv_req = DNS.Request(qtype='srv') srv_req = DNS.Request(qtype='srv')
@ -128,61 +178,55 @@ def parse_input(inp):
""" takes the input from the mcping command and returns the host and port """ """ takes the input from the mcping command and returns the host and port """
inp = inp.strip().split(" ")[0] inp = inp.strip().split(" ")[0]
if ":" in inp: if ":" in inp:
# the port is defined in the input string
host, port = inp.split(":", 1) host, port = inp.split(":", 1)
try: try:
port = int(port) port = int(port)
except: if port > 65535 or port < 0:
raise Exception("The port '{}' is invalid.".format(port)) raise ParseError("The port '{}' is invalid.".format(port))
except ValueError:
raise ParseError("The port '{}' is invalid.".format(port))
return host, port return host, port
if pydns_installed: if has_dns:
srv_data = get_srv_data(inp) # the port is not in the input string, but we have PyDNS so look for a SRV record
srv_data = check_srv(inp)
if srv_data: if srv_data:
return str(srv_data[1]), int(srv_data[0]) return str(srv_data[1]), int(srv_data[0])
# return default port
return inp, 25565 return inp, 25565
@hook.command def format_colors(motd):
@hook.command("mcp6") for original, replacement in mc_colors:
def mcping6(inp): motd = motd.replace(original, replacement)
"""mcping6 <server>[:port] - Ping a Minecraft server version 1.6 or smaller to check status.""" motd = motd.replace(u"\xa7k", "")
#try: return motd
host, port = parse_input(inp)
#except Exception as ex:
# return ex.args[0]
try:
return mcping_legacy(host, port)
except:
return "The 1.6 server {}:{} looks offline from here.".format(host, port)
@hook.command def format_output(data):
@hook.command("mcp7") if data["version"]:
def mcping7(inp): return u"{motd}\x0f - {version}\x0f - {players}/{players_max}" \
"""mcping <server>[:port] - Ping a Minecraft server version 1.7 or greater to check status.""" u" players.".format(**data).replace("\n", u"\x0f - ")
try: else:
host, port = parse_input(inp) return u"{motd}\x0f - {players}/{players_max}" \
except Exception as ex: u" players.".format(**data).replace("\n", u"\x0f - ")
return ex.args[0]
try:
return mcping_modern(host, port)
except:
return "The 1.7 server {}:{} looks offline from here.".format(host, port)
@hook.command @hook.command
@hook.command("mcp") @hook.command("mcp")
def mcping(inp): def mcping(inp):
"""mcping <server>[:port] - Ping a Minecraft server to check status.""" """mcping <server>[:port] - Ping a Minecraft server to check status."""
# try: try:
host, port = parse_input(inp) host, port = parse_input(inp)
#except Exception as e: except ParseError as e:
# return e.args[0] return "Could not parse input ({})".format(e)
#
try: try:
return mcping_modern(host, port) data = mcping_modern(host, port)
except: except PingError:
try: try:
return mcping_legacy(host, port) data = mcping_legacy(host, port)
except: except PingError as e:
return "The server {} ({}:{}) looks offline from here.".format(inp, host, port) return "Could not ping server, is it offline? ({})".format(e)
return format_output(data)

View file

@ -18,37 +18,27 @@ def mcstatus(inp):
out = [] out = []
# use a loop so we don't have to update it if they add more servers # use a loop so we don't have to update it if they add more servers
yes = [] green = []
no = [] yellow = []
red = []
for server, status in data.items(): for server, status in data.items():
if status == "green": if status == "green":
yes.append(server) green.append(server)
elif status == "yellow":
yellow.append(server)
else: else:
no.append(server) red.append(server)
if yes:
out = "\x033\x02Online\x02\x0f: " + ", ".join(yes) if green:
if no: out = "\x033\x02Online\x02\x0f: " + ", ".join(green)
if yellow:
out += " " out += " "
if no: if yellow:
out += "\x034\x02Offline\x02\x0f: " + ", ".join(no) out += "\x02Issues\x02: " + ", ".join(yellow)
if red:
out += " "
if red:
out += "\x034\x02Offline\x02\x0f: " + ", ".join(red)
return "\x0f" + out.replace(".mojang.com", ".mj") \ return "\x0f" + out.replace(".mojang.com", ".mj") \
.replace(".minecraft.net", ".mc") .replace(".minecraft.net", ".mc")
@hook.command("haspaid")
@hook.command
def mcpaid(inp):
"""mcpaid <username> -- Checks if <username> has a premium Minecraft account."""
user = inp.strip()
try:
status = http.get("http://www.minecraft.net/haspaid.jsp", user=user)
except (http.URLError, http.HTTPError) as e:
return "Unable to get user registration status: {}".format(e)
if "true" in status:
return 'The account "{}" is a premium Minecraft account!'.format(inp)
else:
return 'The account "{}" is not a premium Minecraft account!'.format(inp)

101
plugins/minecraft_user.py Normal file
View file

@ -0,0 +1,101 @@
import json
from util import hook, http
NAME_URL = "https://account.minecraft.net/buy/frame/checkName/{}"
PAID_URL = "http://www.minecraft.net/haspaid.jsp"
class McuError(Exception):
pass
def get_status(name):
""" takes a name and returns status """
try:
name_encoded = http.quote_plus(name)
response = http.get(NAME_URL.format(name_encoded))
except (http.URLError, http.HTTPError) as e:
raise McuError("Could not get name status: {}".format(e))
if "OK" in response:
return "free"
elif "TAKEN" in response:
return "taken"
elif "invalid characters" in response:
return "invalid"
def get_profile(name):
profile = {}
# form the profile request
request = {
"name": name,
"agent": "minecraft"
}
# submit the profile request
try:
headers = {"Content-Type": "application/json"}
r = http.get_json(
'https://api.mojang.com/profiles/page/1',
post_data=json.dumps(request),
headers=headers
)
except (http.URLError, http.HTTPError) as e:
raise McuError("Could not get profile status: {}".format(e))
user = r["profiles"][0]
profile["name"] = user["name"]
profile["id"] = user["id"]
profile["legacy"] = user.get("legacy", False)
try:
response = http.get(PAID_URL, user=name)
except (http.URLError, http.HTTPError) as e:
raise McuError("Could not get payment status: {}".format(e))
if "true" in response:
profile["paid"] = True
else:
profile["paid"] = False
return profile
@hook.command("haspaid")
@hook.command("mcpaid")
@hook.command
def mcuser(inp):
"""mcpaid <username> -- Gets information about the Minecraft user <account>."""
user = inp.strip()
try:
# get status of name (does it exist?)
name_status = get_status(user)
except McuError as e:
return e
if name_status == "taken":
try:
# get information about user
profile = get_profile(user)
except McuError as e:
return "Error: {}".format(e)
profile["lt"] = ", legacy" if profile["legacy"] else ""
if profile["paid"]:
return u"The account \x02{name}\x02 ({id}{lt}) exists. It is a \x02paid\x02" \
u" account.".format(**profile)
else:
return u"The account \x02{name}\x02 ({id}{lt}) exists. It \x034\x02is NOT\x02\x0f a paid" \
u" account.".format(**profile)
elif name_status == "free":
return u"The account \x02{}\x02 does not exist.".format(user)
elif name_status == "invalid":
return u"The name \x02{}\x02 contains invalid characters.".format(user)
else:
# if you see this, panic
return "Unknown Error."

View file

@ -3,7 +3,6 @@ from bs4 import BeautifulSoup
from util import hook, http, web from util import hook, http, web
api_url = "http://osrc.dfm.io/{}/stats"
user_url = "http://osrc.dfm.io/{}" user_url = "http://osrc.dfm.io/{}"
@ -12,18 +11,19 @@ def osrc(inp):
"""osrc <github user> -- Gets an Open Source Report Card for <github user>""" """osrc <github user> -- Gets an Open Source Report Card for <github user>"""
user_nick = inp.strip() user_nick = inp.strip()
url = api_url.format(user_nick) url = user_url.format(user_nick)
try: try:
response = http.get_json(url) soup = http.get_soup(url)
except (http.HTTPError, http.URLError): except (http.HTTPError, http.URLError):
return "Couldn't find any stats for this user." return "Couldn't find any stats for this user."
response["nick"] = user_nick report = soup.find("div", {"id": "description"}).find("p").get_text()
soup = BeautifulSoup(response["summary"])
response["work_time"] = soup.find("a", {"href": "#day"}).contents[0]
response["short_url"] = web.try_isgd(user_url.format(user_nick)) # Split and join to remove all the excess whitespace, slice the
# string to remove the trailing full stop.
report = " ".join(report.split())[:-1]
return "{nick} is a {lang_user}. {nick} is a {hacker_type} " \ short_url = web.try_isgd(url)
"who seems to {work_time} - {short_url}".format(**response)
return "{} - {}".format(report, short_url)

View file

@ -38,8 +38,22 @@ def steamcalc(inp, reply=None):
"""steamcalc <username> [currency] - Gets value of steam account and """steamcalc <username> [currency] - Gets value of steam account and
total hours played. Uses steamcommunity.com/id/<nickname>. """ total hours played. Uses steamcommunity.com/id/<nickname>. """
# check if the user asked us to force reload
force_reload = inp.endswith(" forcereload")
if force_reload:
name = inp[:-12].strip().lower()
else:
name = inp.strip() name = inp.strip()
if force_reload:
try:
reply("Collecting data, this may take a while.")
refresh_data(name)
request = get_data(name)
do_refresh = False
except (http.HTTPError, http.URLError):
return "Could not get data for this user."
else:
try: try:
request = get_data(name) request = get_data(name)
do_refresh = True do_refresh = True

27
plugins/suggest.py Normal file
View file

@ -0,0 +1,27 @@
import json
from util import hook, http, text
from bs4 import BeautifulSoup
@hook.command
def suggest(inp):
"""suggest <phrase> -- Gets suggested phrases for a google search"""
page = http.get('http://google.com/complete/search',
output='json', client='hp', q=inp)
page_json = page.split('(', 1)[1][:-1]
suggestions = json.loads(page_json)[1]
suggestions = [suggestion[0] for suggestion in suggestions]
if not suggestions:
return 'no suggestions found'
out = u", ".join(suggestions)
# defuckify text
soup = BeautifulSoup(out)
out = soup.get_text()
return text.truncate_str(out, 200)

View file

@ -6,19 +6,18 @@ import re
from util import hook, timesince from util import hook, timesince
db_ready = False db_ready = []
def db_init(db):
"""check to see that our db has the tell table and return a dbection.""" def db_init(db, conn):
"""Check that our db has the tell table, create it if not."""
global db_ready global db_ready
if not db_ready: if not conn.name in db_ready:
db.execute("create table if not exists tell" db.execute("create table if not exists tell"
"(user_to, user_from, message, chan, time," "(user_to, user_from, message, chan, time,"
"primary key(user_to, message))") "primary key(user_to, message))")
db.commit() db.commit()
db_ready = True db_ready.append(conn.name)
return db
def get_tells(db, user_to): def get_tells(db, user_to):
@ -28,11 +27,11 @@ def get_tells(db, user_to):
@hook.singlethread @hook.singlethread
@hook.event('PRIVMSG') @hook.event('PRIVMSG')
def tellinput(paraml, input=None, notice=None, db=None, bot=None, nick=None, conn=None): def tellinput(inp, input=None, notice=None, db=None, nick=None, conn=None):
if 'showtells' in input.msg.lower(): if 'showtells' in input.msg.lower():
return return
db_init(db) db_init(db, conn)
tells = get_tells(db, nick) tells = get_tells(db, nick)
@ -52,10 +51,10 @@ def tellinput(paraml, input=None, notice=None, db=None, bot=None, nick=None, con
@hook.command(autohelp=False) @hook.command(autohelp=False)
def showtells(inp, nick='', chan='', notice=None, db=None): def showtells(inp, nick='', chan='', notice=None, db=None, conn=None):
"""showtells -- View all pending tell messages (sent in a notice).""" """showtells -- View all pending tell messages (sent in a notice)."""
db_init(db) db_init(db, conn)
tells = get_tells(db, nick) tells = get_tells(db, nick)
@ -74,7 +73,7 @@ def showtells(inp, nick='', chan='', notice=None, db=None):
@hook.command @hook.command
def tell(inp, nick='', chan='', db=None, input=None, notice=None): def tell(inp, nick='', chan='', db=None, input=None, notice=None, conn=None):
"""tell <nick> <message> -- Relay <message> to <nick> when <nick> is around.""" """tell <nick> <message> -- Relay <message> to <nick> when <nick> is around."""
query = inp.split(' ', 1) query = inp.split(' ', 1)
@ -99,10 +98,10 @@ def tell(inp, nick='', chan='', db=None, input=None, notice=None):
return return
if not re.match("^[A-Za-z0-9_|.\-\]\[]*$", user_to.lower()): if not re.match("^[A-Za-z0-9_|.\-\]\[]*$", user_to.lower()):
notice("I cant send a message to that user!") notice("I can't send a message to that user!")
return return
db_init(db) db_init(db, conn)
if db.execute("select count() from tell where user_to=?", if db.execute("select count() from tell where user_to=?",
(user_to,)).fetchone()[0] >= 10: (user_to,)).fetchone()[0] >= 10:

View file

@ -1,28 +1,10 @@
"""
TV information, written by Lurchington 2010
modified by rmmh 2010
"""
import datetime import datetime
from urllib2 import URLError
from zipfile import ZipFile
from cStringIO import StringIO
from lxml import etree
from util import hook, http from util import hook, http
base_url = "http://thetvdb.com/api/" base_url = "http://thetvdb.com/api/"
api_key = "469B73127CA0C411"
def get_zipped_xml(*args, **kwargs):
try:
path = kwargs.pop("path")
except KeyError:
raise KeyError("must specify a path for the zipped file to be read")
zip_buffer = StringIO(http.get(*args, **kwargs))
return etree.parse(ZipFile(zip_buffer, "r").open(path))
def get_episodes_for_series(series_name, api_key): def get_episodes_for_series(series_name, api_key):
@ -30,7 +12,7 @@ def get_episodes_for_series(series_name, api_key):
# http://thetvdb.com/wiki/index.php/API:GetSeries # http://thetvdb.com/wiki/index.php/API:GetSeries
try: try:
query = http.get_xml(base_url + 'GetSeries.php', seriesname=series_name) query = http.get_xml(base_url + 'GetSeries.php', seriesname=series_name)
except URLError: except http.URLError:
res["error"] = "error contacting thetvdb.com" res["error"] = "error contacting thetvdb.com"
return res return res
@ -43,9 +25,8 @@ def get_episodes_for_series(series_name, api_key):
series_id = series_id[0] series_id = series_id[0]
try: try:
series = get_zipped_xml(base_url + '%s/series/%s/all/en.zip' % series = http.get_xml(base_url + '%s/series/%s/all/en.xml' % (api_key, series_id))
(api_key, series_id), path="en.xml") except http.URLError:
except URLError:
res["error"] = "Error contacting thetvdb.com." res["error"] = "Error contacting thetvdb.com."
return res return res

43
plugins/update.py Normal file
View file

@ -0,0 +1,43 @@
from git import Repo
from util import hook, web
@hook.command
def update(inp, bot=None):
repo = Repo()
git = repo.git
try:
pull = git.pull()
except Exception as e:
return e
if "\n" in pull:
return web.haste(pull)
else:
return pull
@hook.command
def version(inp, bot=None):
repo = Repo()
# get origin and fetch it
origin = repo.remotes.origin
info = origin.fetch()
# get objects
head = repo.head
origin_head = info[0]
current_commit = head.commit
remote_commit = origin_head.commit
if current_commit == remote_commit:
in_sync = True
else:
in_sync = False
# output
return "Local \x02{}\x02 is at commit \x02{}\x02, remote \x02{}\x02 is at commit \x02{}\x02." \
" You {} running the latest version.".format(head, current_commit.name_rev[:7],
origin_head, remote_commit.name_rev[:7],
"are" if in_sync else "are not")

View file

@ -52,7 +52,7 @@ def get_json(*args, **kwargs):
def open(url, query_params=None, user_agent=None, post_data=None, def open(url, query_params=None, user_agent=None, post_data=None,
referer=None, get_method=None, cookies=False, timeout=None, **kwargs): referer=None, get_method=None, cookies=False, timeout=None, headers=None, **kwargs):
if query_params is None: if query_params is None:
query_params = {} query_params = {}
@ -68,6 +68,10 @@ def open(url, query_params=None, user_agent=None, post_data=None,
if get_method is not None: if get_method is not None:
request.get_method = lambda: get_method request.get_method = lambda: get_method
if headers is not None:
for header_key, header_value in headers.iteritems():
request.add_header(header_key, header_value)
request.add_header('User-Agent', user_agent) request.add_header('User-Agent', user_agent)
if referer is not None: if referer is not None:

View file

@ -144,7 +144,9 @@ def truncate_words(content, length=10, suffix='...'):
# from <http://stackoverflow.com/questions/250357/smart-truncate-in-python> # from <http://stackoverflow.com/questions/250357/smart-truncate-in-python>
def truncate_str(content, length=100, suffix='...'): def truncate_str(content, length=100, suffix='...'):
"""Truncates a string after a certain number of chars.""" """Truncates a string after a certain number of chars.
@rtype : str
"""
if len(content) <= length: if len(content) <= length:
return content return content
else: else: