Open Table Of Contents

Source code for leekspin.nicknames

# -*- coding: utf-8 -*-

"""Onion Router nickname generator for making descriptors easily searchable."""


from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

from codecs import open as open

import logging
import os
import random
import string

logging.getLogger('leekspin')

#: The dictionary file to read in order to generate the nickname wordlist.
DICTIONARY_FILE          = '/usr/share/dict/words'
#: The maximum allowed length for an OR nickname.
NICKNAME_LENGTH_MAX      = 19
#: The minimum allowed length for an OR nickname.
NICKNAME_LENGTH_MIN      = 1
#: Even if we've already hit th ``NICKNAME_LENGTH_MIN``, try to add more words
#: until the OR nickname is at least this long.
NICKNAME_LENGTH_PREF_MIN = 10


def _createWordsForNicks():
    """Create a list of words used to generate random OR nicknames.

    :rtype: list
    :returns: A list of words suitable for an OR nickname.
    """
    allowedChars = string.letters + string.digits
    wordlist = []
    if os.path.isfile(DICTIONARY_FILE) and os.access(DICTIONARY_FILE, os.R_OK):
        try:
            dictfh = open(DICTIONARY_FILE)
            rawdict = dictfh.readlines()
        except (OSError, IOError):
            logging.error("error opening dictionary")
            return []

        for word in rawdict:
            for char in word:
                if char not in allowedChars:
                    word = word.replace(char, str()).capitalize()
            wordlist.append(word)
    return wordlist


_words = _createWordsForNicks()

MAX_INDEX_WORDS = len(_words) - 1


def _checkWordLength(word, minlength=NICKNAME_LENGTH_MIN, maxlength=NICKNAME_LENGTH_MAX):
    """Returns ``True`` if **word** is an acceptable length."""
    if (int(minlength) < 0) or (int(maxlength) <= 0):
        return False
    if (int(minlength) <= len(str(word)) <= maxlength):
        return True
    return False

def _getCharsNeeded(word, minlength, maxlength):
    minNeeded  = minlength - len(str(word))
    maxNeeded  = maxlength - len(str(word))
    return minNeeded, maxNeeded

def _getRandomWord(minlength=NICKNAME_LENGTH_MIN, maxlength=NICKNAME_LENGTH_MAX):
    pick = lambda: _words[random.randint(0, MAX_INDEX_WORDS)]
    picked = pick()

    while not _checkWordLength(picked, minlength, maxlength):
        picked = pick()
    return picked

[docs]def generateNickname(maxlength=NICKNAME_LENGTH_MAX): """Generate a random alphanumeric nickname conforming to the specified length. :param int maxlength: The maximum length for any OR nickname generated. :rtype: str :returns: A randomly generated relay nickname. """ if len(_words) == 0: # if there was no /usr/share/dict/words file return "Unnamed" nickname = _getRandomWord(NICKNAME_LENGTH_MIN, maxlength) minNeeded, maxNeeded = _getCharsNeeded(nickname, NICKNAME_LENGTH_PREF_MIN, maxlength) while minNeeded > 0: nickname += _getRandomWord(minNeeded, maxNeeded) minNeeded, maxNeeded = _getCharsNeeded(nickname, minNeeded, maxNeeded) else: logging.info("%s (length: %d)" % (nickname, len(nickname))) return nickname