import pytz
import random
import re
+import speake3
import sys
+import tempfile
import time
import traceback
import urllib.request
result = await maybe_play_sound(channel, filename, probability = 1.0, join = join)
return result
+async def tts(channel, text, args = {}):
+ try:
+ fd, wav = tempfile.mkstemp('.tts')
+ os.close(fd)
+ except:
+ log.exception('mkstemp')
+ return False
+ try:
+ engine = speake3.Speake()
+ params = {
+ 'pitch': '97',
+ 'speed': '250',
+ 'voice': 'mb-en1'
+ }
+ params.update(args)
+ for k, v in params.items():
+ log.debug('{} {}'.format(k, v))
+ engine.set(k, v)
+ engine.set('wavfile', wav)
+ engine.say(text)
+ engine.talkback()
+ except:
+ log.exception('speake3')
+ os.unlink(wav)
+ return False
+
+ await play_sound(channel, wav)
+ os.unlink(wav)
+ return True
+
def lower_role_than(member, *, strict = False):
if member.server.me is None:
log.error("Can't call lower_role_than() a member on a server we aren't on!")
class Play(object):
def description(self):
- return 'Play audio files.'
+ return 'Play audio files and text-to-speech.'
def valid_commands(self):
- return ['play']
+ return ['play', 'tts']
async def handle_command(self, message, command, raw):
if command not in self.valid_commands():
return PluginCommand.ignored
- await self.play(message, shlex.split(raw)[1:])
+ if command == 'play':
+ await self.play(message, shlex.split(raw)[1:])
+ elif command == 'tts':
+ parts = raw.split(':')
+ args = shlex.split(parts[0])[1:]
+ if len(parts) > 1:
+ text = ':'.join(parts[1:]).strip()
+ await self.play(message, args, text)
+ elif args[0] == 'help':
+ await self.help_tts(message)
+ else:
+ await bot.say(message.channel, 'what?')
return PluginCommand.exclusive
async def handle_help(self, message, command, *args):
- await self.help_play(message)
+ if command == 'play':
+ await self.help_play(message)
+ elif command == 'tts':
+ await self.help_tts(message)
async def help_play(self, message):
lines = [
]
await bot.say_many(message.channel, lines)
- async def play(self, message, args):
+ async def help_tts(self, message):
+ lines = [
+ 'Text-to-speech:',
+ '```',
+ 'tts in CHANNEL [ARGS]: MESSAGE',
+ 'tts to MEMBER [ARGS]: MESSAGE',
+ '',
+ 'If you do not specify a #channel I will play to the channel you are in.',
+ "If you specify `to` a user I will play to that user's channel instead."
+ '```',
+ 'The `ARGS` to `tts` are one or more of:',
+ '',
+ '`voice VOICE`',
+ '',
+ '`amplitude 0-200` default `100',
+ '',
+ '`pitch 0-99` default `97`',
+ '',
+ '`speed 80-450` default `250`'
+ ]
+ await bot.say_many(message.channel, lines)
+
+ async def play(self, message, args, text = None):
if not len(args):
await bot.maybe_say(message.channel, 'what?', probability = 0.5)
return False
if args[0].lower() == 'help':
- await self.help_play(message)
+ if text is None:
+ await self.help_play(message)
+ else:
+ await self.help_tts(message)
return True
url = None
voice = None
+ tts = {}
i = 0
while i < len(args):
log.info('Parsed user {} is not in any shared voice channel'.format(user))
if voice is not None:
ok = True
- else:
+ elif arg in ['amplitude', 'pitch', 'speed', 'voice']:
+ if param is not None:
+ tts[arg] = param
+ ok = True
+ elif not text:
# arg is lowercase!
url = args[i]
i -= 1
if ok and not voice:
voice = bot.voice_channel_for_user(message.author)
- if not ok or not url or not voice:
+ if not ok or not (url or text) or not voice:
log.info('Failed to parse play command. Got url {} channel {}'.format(url, voice))
if not url:
await bot.say(message.channel, 'what?')
server = voice.server
member = server.get_member(message.author.id)
if bot.is_admin(member, server) or member.top_role >= server.me.top_role:
- log.info('Sound {} requested by {} in {} on {}'.format(url, message.author, voice.name, server.name))
await bot.wake_up()
- result = await bot.play_sound(voice, url)
+ if text:
+ log.info('Text-to-speech request by {} in {} on {}: {}'.format(message.author, voice.name, server.name, text))
+ result = await bot.tts(voice, text, tts)
+ else:
+ log.info('Sound {} requested by {} in {} on {}'.format(url, message.author, voice.name, server.name))
+ result = await bot.play_sound(voice, url)
if not result:
await cat.yelp(message.channel)
else:
- log.info('User {} is not authorised to request sound {} in {} on {}'.format(message.author, url, voice.name, server.name))
+ if text:
+ log.info('User {} is not authorised to request text-to-speech in {} on {}'.format(message.author, voice.name, server.name))
+ else:
+ log.info('User {} is not authorised to request sound {} in {} on {}'.format(message.author, url, voice.name, server.name))
await cat.hiss(message.channel)