Ao desenvolver bots para o Telegram e outros mensageiros, a tarefa de reconhecer e atender solicitações expressas em linguagem humana surge periodicamente. É esse "recurso", de acordo com algumas opiniões, a principal diferença entre os bots e os aplicativos de linha de comando. Abaixo do corte, uma estrutura proprietária para executar comandos de voz arbitrários é descrita. As descrições dos conceitos-chave são acompanhadas por exemplos na linguagem Kotlin.
Vamos tomar representações semânticas normalizadas como base para o reconhecimento de voz. Sua escolha se deve principalmente à simplicidade e facilidade de implementação. Vamos começar com o básico, um exemplo das fontes do framework:
/** */
typealias Rule = (String) -> Boolean
/** */
open class Semnorm(vararg val rules: Rule)
/** */
fun stem(vararg stems: String): Rule = { stems.any(it::startsWith) }
/** */
fun word(vararg words: String): Rule = { words.any(it::equals) }
/** */
fun String.matches(norm: Semnorm) = norm.rules.any { it(this) }
Agora temos a capacidade de definir representações semânticas normalizadas predefinidas na forma de objetos:
object Day : Semnorm(stem("day", "", "", "", "", ""))
A estrutura os coloca em linha com os tokens das frases recebidas, e a frase começa a ficar assim:
assertThat(
" 5 ".tokenize(),
equalTo(
listOf(
Token("", Ban),
Token("", null),
Token("", null),
Token("5", Number),
Token("", Minute)
)
)
)
Já tratamos do reconhecimento de voz. O código do tokenizer está anexado no repositório disponível no final do artigo. Vamos prosseguir para a execução dos comandos da fala. E é aqui que começa a coisa mais interessante: o framework permite que cada representação semântica bloqueie o comportamento especificado. Novamente, o exemplo mais simples de como reconhecer um pedido de ajuda em dois idiomas:
object Help : ExecutableSemnorm(stem(
"", "", "", "help",
"rule", "faq", "start", "",
)) {
override fun execute(bot: Bot, m: Message) {
val faq = message.from.relatedFaq()
bot.sendMessage(m.chat.id, faq)
}
}
, ? , , , , :
object Ban : DurableSemonrm(stem(
"ban", "block", "mute", "", "",
"", "", "",
)) {
override fun execute(
bot: Bot, attackerMessage: Message, duration: Duration) {
val victimMessage = attackerMessage.replyToMessage
val victimId = victimMessage.from.id
val untilSecond = now().epochSecond + duration.inWholeSeconds
bot.restrictChatMember(
attackerMessage.chat.id, victimId, untilSecond)
}
}
? , . , :
object Week : Semnorm(stem("week", "")) {
override fun toDuration(number: Long) =
days(number) * 7
}
, :
class DurableSemnorm(vararg rules: Rule) : ExecutableSemnorm(*rules) {
final override fun execute(
token: Iterator<Token>, bot: Bot, m: Message) =
execute(bot, message, token.parseDuration())
abstract fun execute(bot: Bot, m: Message, duration: Duration)
}
, . . , , Github.