Browse Source

A number of fixes; color and stdout flushing.

tags/v0.1^2
Ben Kurtovic 11 years ago
parent
commit
110e83fb6b
2 changed files with 46 additions and 21 deletions
  1. +12
    -7
      earwigbot/config/__init__.py
  2. +34
    -14
      earwigbot/config/script.py

+ 12
- 7
earwigbot/config/__init__.py View File

@@ -117,6 +117,15 @@ class BotConfig(object):
"""Return a nice string representation of the BotConfig.""" """Return a nice string representation of the BotConfig."""
return "<BotConfig at {0}>".format(self.root_dir) return "<BotConfig at {0}>".format(self.root_dir)


def _handle_missing_config(self):
print "Config file missing or empty:", self._config_path
msg = "Would you like to create a config file now? [Y/n] "
choice = raw_input(msg)
if choice.lower().startswith("n"):
raise NoConfigError()
else:
ConfigScript(self).make_new()

def _load(self): def _load(self):
"""Load data from our JSON config file (config.yml) into self._data.""" """Load data from our JSON config file (config.yml) into self._data."""
filename = self._config_path filename = self._config_path
@@ -263,15 +272,11 @@ class BotConfig(object):
decrypted if they were decrypted earlier. decrypted if they were decrypted earlier.
""" """
if not path.exists(self._config_path): if not path.exists(self._config_path):
print "Config file not found:", self._config_path
choice = raw_input("Would you like to create a config file now? [Y/n] ")
if choice.lower().startswith("n"):
raise NoConfigError()
else:
ConfigScript(self).make_new()

self._handle_missing_config()
self._load() self._load()
data = self._data data = self._data
if not data:
self._handle_missing_config()
self.components._load(data.get("components", OrderedDict())) self.components._load(data.get("components", OrderedDict()))
self.wiki._load(data.get("wiki", OrderedDict())) self.wiki._load(data.get("wiki", OrderedDict()))
self.irc._load(data.get("irc", OrderedDict())) self.irc._load(data.get("irc", OrderedDict()))


+ 34
- 14
earwigbot/config/script.py View File

@@ -26,6 +26,7 @@ from hashlib import sha256
from os import chmod, mkdir, path from os import chmod, mkdir, path
import re import re
import stat import stat
import sys
from textwrap import fill, wrap from textwrap import fill, wrap


try: try:
@@ -60,6 +61,7 @@ def process(bot, rc):
class ConfigScript(object): class ConfigScript(object):
"""A script to guide a user through the creation of a new config file.""" """A script to guide a user through the creation of a new config file."""
WIDTH = 79 WIDTH = 79
PROMPT = "\x1b[32m> \x1b[0m"
BCRYPT_ROUNDS = 12 BCRYPT_ROUNDS = 12


def __init__(self, config): def __init__(self, config):
@@ -82,24 +84,28 @@ class ConfigScript(object):
def _print(self, text): def _print(self, text):
print fill(re.sub("\s\s+", " ", text), self.WIDTH) print fill(re.sub("\s\s+", " ", text), self.WIDTH)


def _print_no_nl(self, text):
sys.stdout.write(fill(re.sub("\s\s+", " ", text), self.WIDTH))
sys.stdout.flush()

def _pause(self): def _pause(self):
raw_input("> Press enter to continue: ")
raw_input(self.PROMPT + "Press enter to continue: ")


def _ask(self, text, default=None): def _ask(self, text, default=None):
text = "> " + text
text = self.PROMPT + text
if default: if default:
text += " [{0}]".format(default)
text += " \x1b[33m[{0}]\x1b[0m".format(default)
lines = wrap(re.sub("\s\s+", " ", text), self.WIDTH) lines = wrap(re.sub("\s\s+", " ", text), self.WIDTH)
if len(lines) > 1: if len(lines) > 1:
print "\n".join(lines[:-1]) print "\n".join(lines[:-1])
return raw_input(lines[-1] + " ") or default return raw_input(lines[-1] + " ") or default


def _ask_bool(self, text, default=True): def _ask_bool(self, text, default=True):
text = "> " + text
text = self.PROMPT + text
if default: if default:
text += " [Y/n]"
text += " \x1b[33m[Y/n]\x1b[0m"
else: else:
text += " [y/N]"
text += " \x1b[33m[y/N]\x1b[0m"
lines = wrap(re.sub("\s\s+", " ", text), self.WIDTH) lines = wrap(re.sub("\s\s+", " ", text), self.WIDTH)
if len(lines) > 1: if len(lines) > 1:
print "\n".join(lines[:-1]) print "\n".join(lines[:-1])
@@ -113,7 +119,7 @@ class ConfigScript(object):
return False return False


def _ask_pass(self, text): def _ask_pass(self, text):
password = getpass("> " + text + " ")
password = getpass(self.PROMPT + text + " ")
if self._cipher: if self._cipher:
mod = len(password) % 8 mod = len(password) % 8
if mod: if mod:
@@ -123,11 +129,11 @@ class ConfigScript(object):
return password return password


def _ask_list(self, text): def _ask_list(self, text):
print fill(re.sub("\s\s+", " ", "> " + text), self.WIDTH)
print fill(re.sub("\s\s+", " ", self.PROMPT + text), self.WIDTH)
print "[one item per line; blank line to end]:" print "[one item per line; blank line to end]:"
result = [] result = []
while True: while True:
line = raw_input("> ")
line = raw_input(self.PROMPT)
if line: if line:
result.append(line) result.append(line)
else: else:
@@ -144,8 +150,9 @@ class ConfigScript(object):
the bot may be annoying.""") the bot may be annoying.""")
if self._ask_bool("Encrypt stored passwords?"): if self._ask_bool("Encrypt stored passwords?"):
self.data["metadata"]["encryptPasswords"] = True self.data["metadata"]["encryptPasswords"] = True
key = getpass("> Enter an encryption key: ")
print "Running {0} rounds of bcrypt...".format(self.BCRYPT_ROUNDS), # STDOUT
key = getpass(self.PROMPT + "Enter an encryption key: ")
msg = "Running {0} rounds of bcrypt...".format(self.BCRYPT_ROUNDS)
self._print_no_nl(msg)
signature = bcrypt.hashpw(key, bcrypt.gensalt(self.BCRYPT_ROUNDS)) signature = bcrypt.hashpw(key, bcrypt.gensalt(self.BCRYPT_ROUNDS))
self.data["metadata"]["signature"] = signature self.data["metadata"]["signature"] = signature
self._cipher = Blowfish.new(sha256(key).digest()) self._cipher = Blowfish.new(sha256(key).digest())
@@ -153,6 +160,7 @@ class ConfigScript(object):
else: else:
self.data["metadata"]["encryptPasswords"] = False self.data["metadata"]["encryptPasswords"] = False


print
self._print("""The bot can temporarily store its logs in the logs/ self._print("""The bot can temporarily store its logs in the logs/
subdirectory. Error logs are kept for a month whereas subdirectory. Error logs are kept for a month whereas
normal logs are kept for a week. If you disable this, normal logs are kept for a week. If you disable this,
@@ -186,11 +194,12 @@ class ConfigScript(object):


def _login(self, kwargs): def _login(self, kwargs):
self.config.wiki._load(self.data["wiki"]) self.config.wiki._load(self.data["wiki"])
print "Trying to login to the site...",
self._print_no_nl("Trying to connect to the site...")
try: try:
site = self.config.bot.wiki.add_site(**kwargs) site = self.config.bot.wiki.add_site(**kwargs)
except exceptions.APIError:
except exceptions.APIError as exc:
print " API error!" print " API error!"
print "\x1b[31m" + exc.message + "\x1b[0m"
question = "Would you like to re-enter the site information?" question = "Would you like to re-enter the site information?"
if self._ask_bool(question): if self._ask_bool(question):
return self._set_wiki() return self._set_wiki()
@@ -198,8 +207,9 @@ class ConfigScript(object):
if self._ask_bool(question, default=False): if self._ask_bool(question, default=False):
raise exceptions.NoConfigError() raise exceptions.NoConfigError()
return self._set_wiki() return self._set_wiki()
except exceptions.LoginError:
except exceptions.LoginError as exc:
print " login error!" print " login error!"
print "\x1b[31m" + exc.message + "\x1b[0m"
question = "Would you like to re-enter your login information?" question = "Would you like to re-enter your login information?"
if self._ask_bool(question): if self._ask_bool(question):
self.data["wiki"]["username"] = self._ask("Bot username:") self.data["wiki"]["username"] = self._ask("Bot username:")
@@ -210,6 +220,13 @@ class ConfigScript(object):
return self._set_wiki() return self._set_wiki()
self._print("""Moving on. You can modify the login information self._print("""Moving on. You can modify the login information
stored in the bot's config in the future.""") stored in the bot's config in the future.""")
password = self.data["wiki"]["password"]
self.data["wiki"]["password"] = None # Clear so we don't login
self.config.wiki._load(self.data["wiki"])
self._print_no_nl("Trying to connect to the site...")
site = self.config.bot.wiki.add_site(**kwargs)
print " success."
self.data["wiki"]["password"] = password # Reset original value
else: else:
print " success." print " success."
return site return site
@@ -252,6 +269,7 @@ class ConfigScript(object):
self.data["wiki"]["shutoff"] = {} self.data["wiki"]["shutoff"] = {}
msg = "Would you like to enable an automatic shutoff page for the bot?" msg = "Would you like to enable an automatic shutoff page for the bot?"
if self._ask_bool(msg): if self._ask_bool(msg):
print
self._print("""The page title can contain two wildcards: $1 will be self._print("""The page title can contain two wildcards: $1 will be
substituted with the bot's username, and $2 with the substituted with the bot's username, and $2 with the
current task number. This can be used to implement a current task number. This can be used to implement a
@@ -282,6 +300,7 @@ class ConfigScript(object):
frontend["nickservPassword"] = ns_pass frontend["nickservPassword"] = ns_pass
chan_question = "Frontend channels to join by default:" chan_question = "Frontend channels to join by default:"
frontend["channels"] = self._ask_list(chan_question) frontend["channels"] = self._ask_list(chan_question)
print
self._print("""The bot keeps a database of its admins (users who self._print("""The bot keeps a database of its admins (users who
can use certain sensitive commands) and owners can use certain sensitive commands) and owners
(users who can quit the bot and modify its access (users who can quit the bot and modify its access
@@ -327,6 +346,7 @@ class ConfigScript(object):
else: else:
chan_question = "Watcher channels to join by default:" chan_question = "Watcher channels to join by default:"
watcher["channels"] = self._ask_list(chan_question) watcher["channels"] = self._ask_list(chan_question)
print
self._print("""I am now creating a blank 'rules.py' file, which self._print("""I am now creating a blank 'rules.py' file, which
will determine how the bot handles messages received will determine how the bot handles messages received
from the IRC watcher. It contains a process() from the IRC watcher. It contains a process()


Loading…
Cancel
Save