199 lines
6 KiB
Python
Executable file
199 lines
6 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#-*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Get a fortune from a JSON file and say it.
|
|
|
|
File is under EUPL1.2
|
|
Author: kujiu
|
|
"""
|
|
|
|
import json
|
|
import argparse
|
|
import logging
|
|
import threading
|
|
|
|
import sys
|
|
import os.path
|
|
import secrets
|
|
try:
|
|
import speechd
|
|
except ImportError:
|
|
speechd = None
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class JsonFortune:
|
|
def __init__(self, srcfile, speak, module, rate, cycle, timer):
|
|
"""
|
|
Fortune software
|
|
"""
|
|
self.speak = speak
|
|
self.module = module
|
|
self.rate = rate
|
|
self.cycle = cycle
|
|
self.timer = timer
|
|
if not module:
|
|
module = 'default'
|
|
|
|
with open(srcfile) as fin:
|
|
self.quotes = json.load(fin)
|
|
|
|
by_category = getattr(options, 'by_category', False)
|
|
if not by_category:
|
|
self.uncategorized_quotes = {}
|
|
for lang in self.quotes:
|
|
self.uncategorized_quotes.setdefault(lang, [])
|
|
for category in self.quotes[lang]:
|
|
for author in self.quotes[lang][category]:
|
|
self.uncategorized_quotes[lang].extend(
|
|
[
|
|
(quote, author)
|
|
for quote
|
|
in self.quotes[lang][category][author]
|
|
]
|
|
)
|
|
|
|
def get_quote(self):
|
|
"""
|
|
Get a quote, returns (quote, lang, author)
|
|
"""
|
|
by_category = getattr(options, 'by_category', False)
|
|
quote = ""
|
|
count = 0
|
|
while not quote and count < 50:
|
|
count += 1
|
|
lang = secrets.choice([lang for lang in self.quotes])
|
|
if by_category:
|
|
category = secrets.choice([cat for cat in self.quotes[lang]])
|
|
if not self.quotes[lang][category]:
|
|
continue
|
|
author = secrets.choice(
|
|
[author for author in self.quotes[lang][category]])
|
|
if not self.quotes[lang][category][author]:
|
|
continue
|
|
quote = secrets.choice(self.quotes[lang][category][author])
|
|
else:
|
|
quote, author = secrets.choice(self.uncategorized_quotes[lang])
|
|
|
|
return (quote, lang, author)
|
|
|
|
def say_quote(self, callback=None):
|
|
"""
|
|
Say a random quote
|
|
"""
|
|
quote, lang, author = self.get_quote()
|
|
|
|
if not speechd:
|
|
logger.error("Speech Dispatcher is not loaded.")
|
|
return
|
|
|
|
if not quote:
|
|
logger.error("No quote found")
|
|
return
|
|
|
|
quote += "\n %s" % author
|
|
|
|
speech_client = speechd.client.Client()
|
|
speech_client.set_priority('important')
|
|
speech_client.set_rate(self.rate)
|
|
if self.module != 'default' and self.module:
|
|
speech_client.set_output_module(self.module)
|
|
|
|
voices = [
|
|
voice[0]
|
|
for voice
|
|
in speech_client.list_synthesis_voices()
|
|
if voice[1] == lang]
|
|
speech_client.set_language(lang)
|
|
speech_client.set_synthesis_voice(secrets.choice(voices))
|
|
logger.info("Say: %s", quote)
|
|
speech_client.speak(quote, callback)
|
|
speech_client.close()
|
|
|
|
def print_quote(self):
|
|
"""
|
|
Print a random quote.
|
|
"""
|
|
if self.cycle:
|
|
print(chr(27) + "[2J")
|
|
|
|
quote_dict = self.get_quote()
|
|
if not quote_dict:
|
|
logger.error("No quote found")
|
|
return
|
|
|
|
print(quote_dict[0] + "\n -- " + quote_dict[2])
|
|
|
|
def run(self):
|
|
"""
|
|
Run
|
|
"""
|
|
if self.speak:
|
|
self.say_quote(self.run if self.cycle else None)
|
|
else:
|
|
self.print_quote()
|
|
if self.cycle:
|
|
threading.Timer(self.timer, self.run).start()
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description="Print/say a quote from JSON file.")
|
|
parser.add_argument(
|
|
'-s', '--source', default='~/.fortunes.json',
|
|
help='JSON file with quotes')
|
|
parser.add_argument(
|
|
'-b', '--by_category', default=False, action='store_true',
|
|
help='Each category has the same weight')
|
|
parser.add_argument(
|
|
'-v', '--verbose', default=False, action='store_true',
|
|
help='Verbose')
|
|
parser.add_argument(
|
|
'-k', '--speak', default=False, action='store_true',
|
|
help='Say with Speech Dispatcher, otherwise print it')
|
|
parser.add_argument(
|
|
'-m', '--module', default='default',
|
|
help='Speech Dispatcher output module')
|
|
parser.add_argument(
|
|
'-r', '--rate', default=15,
|
|
help='Speech Dispatcher rate', type=int)
|
|
parser.add_argument(
|
|
'-c', '--cycle', default=False, action='store_true',
|
|
help='Display/say fortunes in loop.')
|
|
parser.add_argument(
|
|
'-t', '--timer', default=60,
|
|
help='Number of seconds between two quotes', type=int)
|
|
|
|
options = parser.parse_args()
|
|
|
|
source = os.path.expanduser(
|
|
os.path.expandvars(options.source)
|
|
)
|
|
|
|
speak = getattr(options, 'speak', False)
|
|
module = getattr(options, 'module', None)
|
|
rate = getattr(options, 'rate', 15)
|
|
verbose = getattr(options, 'verbose', False)
|
|
cycle = getattr(options, 'cycle', False)
|
|
|
|
ch = logging.StreamHandler(sys.stderr)
|
|
ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
|
|
ch.setLevel(logging.WARNING)
|
|
logger.addHandler(ch)
|
|
|
|
class VerboseFilter(logging.Filter):
|
|
def filter(self, rec):
|
|
return rec.levelno in (logging.DEBUG, logging.INFO)
|
|
|
|
if verbose:
|
|
ch = logging.StreamHandler(sys.stdout)
|
|
ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
|
|
ch.setLevel(logging.DEBUG)
|
|
ch.addFilter(VerboseFilter())
|
|
logger.addHandler(ch)
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
logger.addHandler(ch)
|
|
|
|
fortune = JsonFortune(source, speak, module, rate, cycle, options.timer)
|
|
fortune.run()
|