Skip to content

Instantly share code, notes, and snippets.

@mickael9
Forked from zopieux/main.py
Last active April 26, 2016 12:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mickael9/fc4b28566a4f06a964dd3807f4a80777 to your computer and use it in GitHub Desktop.
Save mickael9/fc4b28566a4f06a964dd3807f4a80777 to your computer and use it in GitHub Desktop.
IRC to Voxygen TTS with streaming to Icecast
import asyncio
import binascii
import re
import subprocess
import time
import threading
import json
import random
from queue import Queue
import requests
import irc3
from irc3.plugins.command import command
API_URL = 'https://www.voxygen.fr/sites/all/modules/voxygen_voices/assets/proxy/index.php'
VOICES =[
"Emma",
"Matteo",
"Michel",
"Fabienne",
"Agnes",
"Becool",
"Electra",
"Helene",
"Loic",
"Melodine",
"Philippe",
"Sorciere",
"Moussa",
"Mendoo"
]
REPLACEMENTS = (
(r'[a-z]{3,6}://[^\s]*', 'URL'),
(r':-?DDD+', 'sourire magistral'),
(r':-?DD', 'énorme sourire'),
(r':-?D', 'grand sourire'),
(r':-?\)', 'sourire'),
(r':-?\(', 'triste'),
(r":-?'", 'pleure'),
(r':-?p', 'tire la langue'),
(r'/', 'slash'),
)
REPLACEMENTS = tuple(
(re.compile(regex, flags=re.I), (', ' + repl + ', )'))
for regex, repl in REPLACEMENTS
)
DATA_FILE = 'data.json'
@irc3.plugin
class TTSPlugin:
requires = [
'irc3.plugins.core',
'irc3.plugins.command',
]
def __init__(self, bot):
self.bot = bot
self.streamer = Streamer()
try:
self.load_data(DATA_FILE)
except:
self.data = {}
self.data['voices'] = self.data.get('voices', {})
self.data['seed'] = self.data.get('seed', random.getrandbits(32))
self.init_voices()
def init_voices(self):
random.seed(self.data['seed'])
random.shuffle(VOICES)
def load_data(self, data_file):
with open(data_file, 'r', encoding='utf-8') as f:
self.data = json.load(f)
def save_data(self, data_file):
with open(data_file, 'w', encoding='utf-8') as f:
json.dump(self.data, f)
@command
def reroll(self, mask, target, args):
"""Re-roll voices
%%reroll
"""
if target not in self.bot.config['autojoins']:
return
self.data['seed'] = random.getrandbits(32)
self.init_voices()
self.save_data(DATA_FILE)
self.bot.privmsg(target, 'Voices shuffled')
@command
def setvoice(self, mask, target, args):
"""Set your TTS voice
%%setvoice <voice>
"""
nick = mask.nick
voice = args['<voice>']
if voice not in VOICES:
self.bot.privmsg(target, "No such voice")
else:
self.data['voices'][mask] = voice
self.save_data(DATA_FILE)
self.bot.privmsg(target, "Your voice was set to %s" % voice)
@irc3.event(irc3.rfc.PRIVMSG)
def on_privmsg(self, mask=None, data=None, target=None, **kw):
if target not in self.bot.config['autojoins']:
return
for regex, repl in REPLACEMENTS:
data = regex.sub(repl, data)
text = data
if mask in self.data['voices']:
voice = self.data['voices'][mask]
else:
voice = VOICES[(binascii.crc32(mask.nick.encode('utf8'))) % len(VOICES)]
r = requests.get(API_URL, params={
'text': text,
'voice': voice,
'ts': int(time.time()),
'method': 'redirect',
})
self.streamer.add_data(r.content)
class Streamer:
def __init__(self):
self.queue = Queue()
self.thread = threading.Thread(target=self.run, daemon=True)
self.thread.start()
def run(self):
while True:
data = self.queue.get()
p = subprocess.Popen(['ezstream', '-c', 'ezstream_mp3.xml'],
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
p.communicate(data)
def add_data(self, data):
self.queue.put(data)
def main():
import sys
nick = sys.argv[1]
chan = sys.argv[2]
loop = asyncio.get_event_loop()
config = {
'autojoins': [chan],
'host': 'irc.freenode.net', 'port': 7000, 'ssl': True,
'includes': [
'irc3.plugins.core',
__name__,
],
'loop': loop
}
irc3.IrcBot(nick=nick, **config).run(forever=False)
loop.run_forever()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment