2016-05-09 20:45:02 +00:00
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
package babbler
import (
2017-05-10 12:41:41 +00:00
"database/sql"
2017-05-09 23:41:40 +00:00
"errors"
2016-05-09 20:45:02 +00:00
"fmt"
2017-05-09 23:41:40 +00:00
"log"
2016-05-09 20:45:02 +00:00
"math/rand"
"strings"
"github.com/jmoiron/sqlx"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
2016-05-11 01:15:52 +00:00
"github.com/velour/catbase/config"
2016-05-09 20:45:02 +00:00
)
2017-05-10 13:42:27 +00:00
var (
2017-05-17 14:06:10 +00:00
NO_BABBLER = errors . New ( "babbler not found" )
2017-05-10 13:42:27 +00:00
SAID_NOTHING = errors . New ( "hasn't said anything yet" )
2017-05-17 14:06:10 +00:00
NEVER_SAID = errors . New ( "never said that" )
2017-05-10 12:41:41 +00:00
)
2016-05-09 20:45:02 +00:00
type BabblerPlugin struct {
2017-05-09 23:41:40 +00:00
Bot bot . Bot
db * sqlx . DB
config * config . Config
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
type Babbler struct {
BabblerId int64 ` db:"id" `
Name string ` db:"babbler" `
}
type BabblerWord struct {
WordId int64 ` db:"id" `
Word string ` db:"word" `
}
type BabblerNode struct {
NodeId int64 ` db:"id" `
BabblerId int64 ` db:"babblerId" `
WordId int64 ` db:"wordId" `
Root int64 ` db:"root" `
RootFrequency int64 ` db:"rootFrequency" `
}
type BabblerArc struct {
ArcId int64 ` db:"id" `
FromNodeId int64 ` db:"fromNodeId" `
ToNodeId int64 ` db:"toNodeId" `
Frequency int64 ` db:"frequency" `
}
2017-05-09 23:41:40 +00:00
func New ( bot bot . Bot ) * BabblerPlugin {
log . SetFlags ( log . LstdFlags | log . Lshortfile )
if bot . DBVersion ( ) == 1 {
if _ , err := bot . DB ( ) . Exec ( ` create table if not exists babblers (
id integer primary key ,
babbler string
) ; ` ) ; err != nil {
log . Fatal ( err )
}
2017-05-10 15:59:35 +00:00
2017-05-09 23:41:40 +00:00
if _ , err := bot . DB ( ) . Exec ( ` create table if not exists babblerWords (
2017-05-10 15:59:35 +00:00
id integer primary key ,
word string
) ; ` ) ; err != nil {
log . Fatal ( err )
}
if _ , err := bot . DB ( ) . Exec ( ` create table if not exists babblerNodes (
2017-05-09 23:41:40 +00:00
id integer primary key ,
babblerId integer ,
2017-05-10 15:59:35 +00:00
wordId integer ,
2017-05-09 23:41:40 +00:00
root integer ,
rootFrequency integer
) ; ` ) ; err != nil {
log . Fatal ( err )
}
2016-05-09 20:45:02 +00:00
2017-05-09 23:41:40 +00:00
if _ , err := bot . DB ( ) . Exec ( ` create table if not exists babblerArcs (
id integer primary key ,
2017-05-17 14:06:10 +00:00
fromNodeId integer ,
toNodeId interger ,
2017-05-09 23:41:40 +00:00
frequency integer
) ; ` ) ; err != nil {
log . Fatal ( err )
}
}
2016-05-09 20:45:02 +00:00
plugin := & BabblerPlugin {
2017-05-09 23:41:40 +00:00
Bot : bot ,
db : bot . DB ( ) ,
config : bot . Config ( ) ,
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
plugin . createNewWord ( "" )
2016-05-11 01:15:52 +00:00
return plugin
}
2016-05-09 20:45:02 +00:00
func ( p * BabblerPlugin ) Message ( message msg . Message ) bool {
lowercase := strings . ToLower ( message . Body )
tokens := strings . Fields ( lowercase )
2016-10-19 23:34:16 +00:00
numTokens := len ( tokens )
2016-05-09 20:45:02 +00:00
2017-05-10 13:42:27 +00:00
saidSomething := false
saidWhat := ""
2017-05-09 23:41:40 +00:00
2017-06-08 00:02:18 +00:00
if numTokens > 2 && tokens [ 1 ] == "says-bridge" && strings . Contains ( lowercase , "|" ) {
split := strings . Split ( lowercase , "|" )
2017-06-07 00:03:07 +00:00
start := strings . Fields ( split [ 0 ] )
end := strings . Fields ( split [ 1 ] )
saidWhat , saidSomething = p . getBabbleWithBookends ( start , end )
} else if numTokens >= 2 && tokens [ 1 ] == "says" {
2017-05-10 13:42:27 +00:00
saidWhat , saidSomething = p . getBabble ( tokens )
2017-06-08 00:02:18 +00:00
} else if numTokens > 2 && tokens [ 1 ] == "says-tail" {
2017-06-05 23:53:19 +00:00
saidWhat , saidSomething = p . getBabbleWithSuffix ( tokens )
2017-06-08 00:02:42 +00:00
} else if numTokens >= 2 && tokens [ 1 ] == "says-middle-out" {
saidWhatStart , saidSomethingStart := p . getBabbleWithSuffix ( tokens )
2017-06-16 19:02:28 +00:00
if ! saidSomethingStart || saidWhatStart == strings . Join ( tokens [ 2 : ] , " " ) {
saidSomething = saidSomethingStart
saidWhat = saidWhatStart
} else {
saidWhatEnd , saidSomethingEnd := p . getBabble ( tokens )
saidSomething = saidSomethingStart && saidSomethingEnd
if saidSomething {
saidWhat = saidWhatStart + " " + strings . Join ( strings . Fields ( saidWhatEnd ) [ len ( tokens ) - 2 : ] , " " )
}
2017-06-08 00:02:42 +00:00
}
} else if len ( tokens ) == 4 && strings . Index ( lowercase , "initialize babbler for " ) == 0 {
2017-05-10 13:42:27 +00:00
saidWhat , saidSomething = p . initializeBabbler ( tokens )
2016-05-11 17:07:16 +00:00
} else if strings . Index ( lowercase , "batch learn for " ) == 0 {
2017-05-10 13:42:27 +00:00
saidWhat , saidSomething = p . batchLearn ( tokens )
2016-05-26 15:06:22 +00:00
} else if len ( tokens ) == 5 && strings . Index ( lowercase , "merge babbler" ) == 0 {
2017-05-10 13:42:27 +00:00
saidWhat , saidSomething = p . merge ( tokens )
2016-05-11 16:11:21 +00:00
} else {
2017-05-10 13:42:27 +00:00
//this should always return "", false
saidWhat , saidSomething = p . addToBabbler ( message . User . Name , lowercase )
2016-05-09 20:45:02 +00:00
}
2017-05-10 13:42:27 +00:00
if saidSomething {
p . Bot . SendMessage ( message . Channel , saidWhat )
}
return saidSomething
2016-05-09 20:45:02 +00:00
}
func ( p * BabblerPlugin ) Help ( channel string , parts [ ] string ) {
2017-06-08 00:02:18 +00:00
commands := [ ] string {
"initialize babbler for seabass" ,
"merge babbler drseabass into seabass" ,
"seabass says ..." ,
"seabass says-tail ..." ,
2017-06-08 00:02:42 +00:00
"seabass says-middle-out ..." ,
2017-06-08 00:02:18 +00:00
"seabass says-bridge ... | ..." ,
}
p . Bot . SendMessage ( channel , strings . Join ( commands , "\n\n" ) )
2016-05-09 20:45:02 +00:00
}
func ( p * BabblerPlugin ) Event ( kind string , message msg . Message ) bool {
return false
}
func ( p * BabblerPlugin ) BotMessage ( message msg . Message ) bool {
return false
}
func ( p * BabblerPlugin ) RegisterWeb ( ) * string {
return nil
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) makeBabbler ( name string ) ( * Babbler , error ) {
res , err := p . db . Exec ( ` insert into babblers (babbler) values (?); ` , name )
2017-05-09 23:41:40 +00:00
if err == nil {
2017-05-17 14:06:10 +00:00
id , err := res . LastInsertId ( )
if err != nil {
log . Print ( err )
return nil , err
}
return & Babbler {
BabblerId : id ,
Name : name ,
} , nil
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-09 23:41:40 +00:00
}
2016-05-09 20:45:02 +00:00
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getBabbler ( name string ) ( * Babbler , error ) {
var bblr Babbler
err := p . db . QueryRowx ( ` select * from babblers where babbler = ? LIMIT 1; ` , name ) . StructScan ( & bblr )
if err != nil {
if err == sql . ErrNoRows {
log . Printf ( "failed to find babbler" )
return nil , NO_BABBLER
}
log . Printf ( "encountered problem in babbler lookup" )
log . Print ( err )
return nil , err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
return & bblr , nil
2017-05-09 23:41:40 +00:00
}
2016-05-09 20:45:02 +00:00
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getOrCreateBabbler ( name string ) ( * Babbler , error ) {
babbler , err := p . getBabbler ( name )
if err == NO_BABBLER {
babbler , err = p . makeBabbler ( name )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
return nil , err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
rows , err := p . db . Queryx ( fmt . Sprintf ( "select tidbit from factoid where fact like '%s quotes';" , babbler . Name ) )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
return babbler , nil
2017-05-09 23:41:40 +00:00
}
2017-05-10 13:50:37 +00:00
defer rows . Close ( )
2017-05-10 12:41:41 +00:00
2017-05-10 13:50:37 +00:00
tidbits := [ ] string { }
2017-05-09 23:41:40 +00:00
for rows . Next ( ) {
var tidbit string
err := rows . Scan ( & tidbit )
2017-05-17 14:06:10 +00:00
log . Print ( tidbit )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
return babbler , err
2016-05-09 20:45:02 +00:00
}
2017-05-10 13:50:37 +00:00
tidbits = append ( tidbits , tidbit )
}
for _ , tidbit := range tidbits {
2017-05-17 14:06:10 +00:00
if err = p . addToMarkovChain ( babbler , tidbit ) ; err != nil {
log . Print ( err )
}
2016-05-09 20:45:02 +00:00
}
}
2017-05-17 14:06:10 +00:00
return babbler , err
2017-05-09 23:41:40 +00:00
}
2016-05-09 20:45:02 +00:00
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getWord ( word string ) ( * BabblerWord , error ) {
var w BabblerWord
err := p . db . QueryRowx ( ` select * from babblerWords where word = ? LIMIT 1; ` , word ) . StructScan ( & w )
if err != nil {
if err == sql . ErrNoRows {
return nil , NEVER_SAID
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-10 14:11:49 +00:00
}
2017-05-17 14:06:10 +00:00
return & w , nil
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) createNewWord ( word string ) ( * BabblerWord , error ) {
res , err := p . db . Exec ( ` insert into babblerWords (word) values (?); ` , word )
if err != nil {
log . Print ( err )
return nil , err
}
id , err := res . LastInsertId ( )
if err != nil {
log . Print ( err )
return nil , err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
return & BabblerWord {
WordId : id ,
Word : word ,
} , nil
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getOrCreateWord ( word string ) ( * BabblerWord , error ) {
if w , err := p . getWord ( word ) ; err == NEVER_SAID {
2017-05-10 15:59:35 +00:00
return p . createNewWord ( word )
} else {
2017-05-17 14:06:10 +00:00
if err != nil {
log . Print ( err )
}
return w , err
2017-05-10 15:59:35 +00:00
}
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getBabblerNode ( babbler * Babbler , word string ) ( * BabblerNode , error ) {
w , err := p . getWord ( word )
if err != nil {
return nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
var node BabblerNode
err = p . db . QueryRowx ( ` select * from babblerNodes where babblerId = ? and wordId = ? LIMIT 1; ` , babbler . BabblerId , w . WordId ) . StructScan ( & node )
if err != nil {
if err == sql . ErrNoRows {
return nil , NEVER_SAID
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
return & node , nil
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) createBabblerNode ( babbler * Babbler , word string ) ( * BabblerNode , error ) {
w , err := p . getOrCreateWord ( word )
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
res , err := p . db . Exec ( ` insert into babblerNodes (babblerId, wordId, root, rootFrequency) values (?, ?, 0, 0) ` , babbler . BabblerId , w . WordId )
if err != nil {
log . Print ( err )
return nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
id , err := res . LastInsertId ( )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
return & BabblerNode {
NodeId : id ,
WordId : w . WordId ,
Root : 0 ,
RootFrequency : 0 ,
} , nil
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getOrCreateBabblerNode ( babbler * Babbler , word string ) ( * BabblerNode , error ) {
node , err := p . getBabblerNode ( babbler , word )
if err != nil {
return p . createBabblerNode ( babbler , word )
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
return node , nil
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) incrementRootWordFrequency ( babbler * Babbler , word string ) ( * BabblerNode , error ) {
node , err := p . getOrCreateBabblerNode ( babbler , word )
if err != nil {
log . Print ( err )
return nil , err
}
_ , err = p . db . Exec ( ` update babblerNodes set rootFrequency = rootFrequency + 1, root = 1 where id = ?; ` , node . NodeId )
if err != nil {
log . Print ( err )
return nil , err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
node . RootFrequency += 1
return node , nil
}
2017-05-10 15:59:35 +00:00
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getBabblerArc ( fromNode , toNode * BabblerNode ) ( * BabblerArc , error ) {
var arc BabblerArc
err := p . db . QueryRowx ( ` select * from babblerArcs where fromNodeId = ? and toNodeId = ?; ` , fromNode . NodeId , toNode . NodeId ) . StructScan ( & arc )
if err != nil {
if err == sql . ErrNoRows {
return nil , NEVER_SAID
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
return & arc , nil
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) incrementWordArc ( fromNode , toNode * BabblerNode ) ( * BabblerArc , error ) {
res , err := p . db . Exec ( ` update babblerArcs set frequency = frequency + 1 where fromNodeId = ? and toNodeId = ?; ` , fromNode . NodeId , toNode . NodeId )
2017-05-10 15:59:35 +00:00
if err != nil {
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-09 23:41:40 +00:00
affectedRows := int64 ( 0 )
if err == nil {
affectedRows , _ = res . RowsAffected ( )
}
2017-05-17 14:06:10 +00:00
if affectedRows == 0 {
res , err = p . db . Exec ( ` insert into babblerArcs (fromNodeId, toNodeId, frequency) values (?, ?, 1); ` , fromNode . NodeId , toNode . NodeId )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , err
2016-05-09 20:45:02 +00:00
}
}
2017-05-17 14:06:10 +00:00
return p . getBabblerArc ( fromNode , toNode )
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) incrementFinalWordArcHelper ( babbler * Babbler , node * BabblerNode ) ( * BabblerArc , error ) {
nextNode , err := p . getOrCreateBabblerNode ( babbler , " " )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
return nil , err
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
return p . incrementWordArc ( node , nextNode )
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) addToMarkovChain ( babbler * Babbler , phrase string ) error {
2017-05-09 23:41:40 +00:00
words := strings . Fields ( strings . ToLower ( phrase ) )
2017-05-10 13:42:27 +00:00
if len ( words ) <= 0 {
2017-05-17 14:06:10 +00:00
return nil
2017-05-10 13:42:27 +00:00
}
2017-05-17 14:06:10 +00:00
curNode , err := p . incrementRootWordFrequency ( babbler , words [ 0 ] )
2016-05-09 20:45:02 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return err
2016-05-09 20:45:02 +00:00
}
2017-05-09 23:41:40 +00:00
for i := 1 ; i < len ( words ) ; i ++ {
2017-05-17 14:06:10 +00:00
nextNode , err := p . getOrCreateBabblerNode ( babbler , words [ i ] )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return err
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
_ , err = p . incrementWordArc ( curNode , nextNode )
if err != nil {
log . Print ( err )
return err
}
curNode = nextNode
2017-05-09 23:41:40 +00:00
}
2016-05-09 20:45:02 +00:00
2017-05-17 14:06:10 +00:00
_ , err = p . incrementFinalWordArcHelper ( babbler , curNode )
return err
2017-05-09 23:41:40 +00:00
}
2016-05-09 20:45:02 +00:00
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getWeightedRootNode ( babbler * Babbler ) ( * BabblerNode , * BabblerWord , error ) {
rows , err := p . db . Queryx ( ` select * from babblerNodes where babblerId = ? and root = 1; ` , babbler . BabblerId )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2017-05-09 23:41:40 +00:00
}
defer rows . Close ( )
2017-05-17 14:06:10 +00:00
rootNodes := [ ] * BabblerNode { }
2017-05-09 23:41:40 +00:00
total := int64 ( 0 )
for rows . Next ( ) {
2017-05-17 14:06:10 +00:00
var node BabblerNode
err = rows . StructScan ( & node )
2016-05-09 20:45:02 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
rootNodes = append ( rootNodes , & node )
total += node . RootFrequency
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
if len ( rootNodes ) == 0 {
return nil , nil , SAID_NOTHING
2017-05-10 12:41:41 +00:00
}
2017-05-09 23:41:40 +00:00
which := rand . Int63n ( total )
total = 0
2017-05-17 14:06:10 +00:00
for _ , node := range rootNodes {
total += node . RootFrequency
if total >= which {
var w BabblerWord
err := p . db . QueryRowx ( ` select * from babblerWords where id = ? LIMIT 1; ` , node . WordId ) . StructScan ( & w )
2017-05-10 15:59:35 +00:00
if err != nil {
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
return node , & w , nil
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
2017-05-09 23:41:40 +00:00
}
log . Fatalf ( "shouldn't happen" )
2017-05-17 14:06:10 +00:00
return nil , nil , errors . New ( "failed to find weighted root word" )
2016-10-19 23:34:16 +00:00
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) getWeightedNextWord ( fromNode * BabblerNode ) ( * BabblerNode , * BabblerWord , error ) {
rows , err := p . db . Queryx ( ` select * from babblerArcs where fromNodeId = ?; ` , fromNode . NodeId )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2017-05-09 23:41:40 +00:00
}
defer rows . Close ( )
2017-05-17 14:06:10 +00:00
arcs := [ ] * BabblerArc { }
2017-05-09 23:41:40 +00:00
total := int64 ( 0 )
for rows . Next ( ) {
2017-05-17 14:06:10 +00:00
var arc BabblerArc
err = rows . StructScan ( & arc )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2016-05-09 20:45:02 +00:00
}
2017-05-17 14:06:10 +00:00
arcs = append ( arcs , & arc )
total += arc . Frequency
2017-05-09 23:41:40 +00:00
}
2017-04-07 19:35:18 +00:00
2017-05-17 14:06:10 +00:00
if len ( arcs ) == 0 {
return nil , nil , errors . New ( "missing arcs" )
2017-05-10 12:41:41 +00:00
}
2017-05-09 23:41:40 +00:00
which := rand . Int63n ( total )
total = 0
2017-05-17 14:06:10 +00:00
for _ , arc := range arcs {
2017-04-07 19:35:18 +00:00
2017-05-17 14:06:10 +00:00
total += arc . Frequency
if total >= which {
var node BabblerNode
err := p . db . QueryRowx ( ` select * from babblerNodes where id = ? LIMIT 1; ` , arc . ToNodeId ) . StructScan ( & node )
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2017-05-10 15:59:35 +00:00
}
2017-05-17 14:06:10 +00:00
var w BabblerWord
err = p . db . QueryRowx ( ` select * from babblerWords where id = ? LIMIT 1; ` , node . WordId ) . StructScan ( & w )
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-17 14:06:10 +00:00
return nil , nil , err
2017-04-07 19:35:18 +00:00
}
2017-05-17 14:06:10 +00:00
return & node , & w , nil
2017-04-07 19:35:18 +00:00
}
2017-05-17 14:06:10 +00:00
2017-05-09 23:41:40 +00:00
}
log . Fatalf ( "shouldn't happen" )
2017-05-17 14:06:10 +00:00
return nil , nil , errors . New ( "failed to find weighted next word" )
2017-05-09 23:41:40 +00:00
}
2017-04-07 19:35:18 +00:00
2017-06-05 23:53:19 +00:00
func ( p * BabblerPlugin ) getWeightedPreviousWord ( toNode * BabblerNode ) ( * BabblerNode , * BabblerWord , bool , error ) {
rows , err := p . db . Queryx ( ` select * from babblerArcs where toNodeId = ?; ` , toNode . NodeId )
if err != nil {
log . Print ( err )
return nil , nil , false , err
}
defer rows . Close ( )
arcs := [ ] * BabblerArc { }
total := int64 ( 0 )
for rows . Next ( ) {
var arc BabblerArc
err = rows . StructScan ( & arc )
if err != nil {
log . Print ( err )
return nil , nil , false , err
}
arcs = append ( arcs , & arc )
total += arc . Frequency
}
if len ( arcs ) == 0 {
return nil , nil , true , nil
}
which := rand . Int63n ( total + toNode . RootFrequency )
//terminate the babble
if which >= total {
return nil , nil , true , nil
}
total = 0
for _ , arc := range arcs {
total += arc . Frequency
if total >= which {
var node BabblerNode
err := p . db . QueryRowx ( ` select * from babblerNodes where id = ? LIMIT 1; ` , arc . FromNodeId ) . StructScan ( & node )
if err != nil {
log . Print ( err )
return nil , nil , false , err
}
var w BabblerWord
err = p . db . QueryRowx ( ` select * from babblerWords where id = ? LIMIT 1; ` , node . WordId ) . StructScan ( & w )
if err != nil {
log . Print ( err )
return nil , nil , false , err
}
return & node , & w , false , nil
}
}
log . Fatalf ( "shouldn't happen" )
return nil , nil , false , errors . New ( "failed to find weighted previous word" )
}
2017-06-07 00:03:07 +00:00
func ( p * BabblerPlugin ) verifyPhrase ( babbler * Babbler , phrase [ ] string ) ( * BabblerNode , * BabblerNode , error ) {
curNode , err := p . getBabblerNode ( babbler , phrase [ 0 ] )
if err != nil {
log . Print ( err )
return nil , nil , err
}
firstNode := curNode
for i := 1 ; i < len ( phrase ) ; i ++ {
nextNode , err := p . getBabblerNode ( babbler , phrase [ i ] )
if err != nil {
log . Print ( err )
return nil , nil , err
}
_ , err = p . getBabblerArc ( curNode , nextNode )
if err != nil {
log . Print ( err )
return nil , nil , err
}
curNode = nextNode
}
return firstNode , curNode , nil
}
2017-05-09 23:41:40 +00:00
func ( p * BabblerPlugin ) babble ( who string ) ( string , error ) {
return p . babbleSeed ( who , [ ] string { } )
}
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) babbleSeed ( babblerName string , seed [ ] string ) ( string , error ) {
babbler , err := p . getBabbler ( babblerName )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return "" , nil
}
words := seed
2017-05-17 14:06:10 +00:00
var curNode * BabblerNode
var curWord * BabblerWord
2017-05-09 23:41:40 +00:00
if len ( seed ) == 0 {
2017-05-17 14:06:10 +00:00
curNode , curWord , err = p . getWeightedRootNode ( babbler )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return "" , err
}
2017-05-17 14:06:10 +00:00
words = append ( words , curWord . Word )
2017-05-09 23:41:40 +00:00
} else {
2017-06-07 00:03:07 +00:00
_ , curNode , err = p . verifyPhrase ( babbler , seed )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return "" , err
}
}
2016-05-09 20:45:02 +00:00
2017-05-09 23:41:40 +00:00
for {
2017-05-17 14:06:10 +00:00
curNode , curWord , err = p . getWeightedNextWord ( curNode )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return "" , err
}
2017-05-17 14:06:10 +00:00
if curWord . Word == " " {
2017-05-09 23:41:40 +00:00
break
}
2017-05-17 14:06:10 +00:00
words = append ( words , curWord . Word )
2017-05-25 19:22:58 +00:00
if len ( words ) >= 250 {
break
}
2016-05-09 20:45:02 +00:00
}
2017-05-09 23:41:40 +00:00
return strings . TrimSpace ( strings . Join ( words , " " ) ) , nil
2016-05-11 01:15:52 +00:00
}
2016-05-26 15:06:22 +00:00
2017-05-17 14:06:10 +00:00
func ( p * BabblerPlugin ) mergeBabblers ( intoBabbler , otherBabbler * Babbler , intoName , otherName string ) error {
intoNode , err := p . getOrCreateBabblerNode ( intoBabbler , "<" + intoName + ">" )
2017-05-10 15:59:35 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-10 15:59:35 +00:00
return err
}
2017-05-17 14:06:10 +00:00
otherNode , err := p . getOrCreateBabblerNode ( otherBabbler , "<" + otherName + ">" )
2017-05-10 15:59:35 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-10 15:59:35 +00:00
return err
}
2017-05-17 14:06:10 +00:00
mapping := map [ int64 ] * BabblerNode { }
2016-05-26 15:06:22 +00:00
2017-05-17 14:06:10 +00:00
rows , err := p . db . Queryx ( "select * from babblerNodes where babblerId = ?;" , otherBabbler . BabblerId )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return err
}
defer rows . Close ( )
2017-05-17 14:06:10 +00:00
nodes := [ ] * BabblerNode { }
2017-05-09 23:41:40 +00:00
for rows . Next ( ) {
2017-05-17 14:06:10 +00:00
var node BabblerNode
err = rows . StructScan ( & node )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return err
2016-05-26 15:06:22 +00:00
}
2017-05-17 14:06:10 +00:00
nodes = append ( nodes , & node )
2017-05-09 23:41:40 +00:00
}
2016-05-26 15:06:22 +00:00
2017-05-17 14:06:10 +00:00
for _ , node := range nodes {
var res sql . Result
2017-05-09 23:41:40 +00:00
2017-05-17 14:06:10 +00:00
if node . NodeId == otherNode . NodeId {
node . WordId = intoNode . WordId
2016-05-26 15:06:22 +00:00
}
2017-05-17 14:06:10 +00:00
if node . Root > 0 {
res , err = p . db . Exec ( ` update babblerNodes set rootFrequency = rootFrequency + ?, root = 1 where babblerId = ? and wordId = ?; ` , node . RootFrequency , intoBabbler . BabblerId , node . WordId )
if err != nil {
log . Print ( err )
}
2017-05-09 23:41:40 +00:00
} else {
2017-05-17 14:06:10 +00:00
res , err = p . db . Exec ( ` update babblerNodes set rootFrequency = rootFrequency + ? where babblerId = ? and wordId = ?; ` , node . RootFrequency , intoBabbler . BabblerId , node . WordId )
if err != nil {
log . Print ( err )
}
2017-05-09 23:41:40 +00:00
}
2016-05-26 15:06:22 +00:00
2017-05-10 15:59:35 +00:00
rowsAffected := int64 ( - 1 )
if err == nil {
rowsAffected , _ = res . RowsAffected ( )
}
if err != nil || rowsAffected == 0 {
2017-05-17 14:06:10 +00:00
res , err = p . db . Exec ( ` insert into babblerNodes (babblerId, wordId, root, rootFrequency) values (?,?,?,?) ; ` , intoBabbler . BabblerId , node . WordId , node . Root , node . RootFrequency )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-10 15:59:35 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return err
2016-05-26 15:06:22 +00:00
}
}
2017-05-09 23:41:40 +00:00
2017-05-17 14:06:10 +00:00
var updatedNode BabblerNode
err = p . db . QueryRowx ( ` select * from babblerNodes where babblerId = ? and wordId = ? LIMIT 1; ` , intoBabbler . BabblerId , node . WordId ) . StructScan ( & updatedNode )
if err != nil {
log . Print ( err )
return err
2017-05-10 15:59:35 +00:00
}
2016-05-26 15:06:22 +00:00
2017-05-17 14:06:10 +00:00
mapping [ node . NodeId ] = & updatedNode
2017-05-09 23:41:40 +00:00
}
2017-05-17 14:06:10 +00:00
for oldNodeId , newNode := range mapping {
rows , err := p . db . Queryx ( "select * from babblerArcs where fromNodeId = ?;" , oldNodeId )
2017-05-09 23:41:40 +00:00
if err != nil {
return err
}
defer rows . Close ( )
2017-05-17 14:06:10 +00:00
arcs := [ ] * BabblerArc { }
2016-05-26 15:06:22 +00:00
2017-05-09 23:41:40 +00:00
for rows . Next ( ) {
2017-05-17 14:06:10 +00:00
var arc BabblerArc
err = rows . StructScan ( & arc )
2017-05-09 23:41:40 +00:00
if err != nil {
2017-05-17 14:06:10 +00:00
log . Print ( err )
2017-05-09 23:41:40 +00:00
return err
}
2017-05-17 14:06:10 +00:00
arcs = append ( arcs , & arc )
2016-05-26 15:06:22 +00:00
}
2017-05-09 23:41:40 +00:00
for _ , arc := range arcs {
2017-05-17 14:06:10 +00:00
_ , err := p . incrementWordArc ( newNode , mapping [ arc . ToNodeId ] )
if err != nil {
return err
2016-05-26 15:06:22 +00:00
}
}
}
2017-05-17 14:06:10 +00:00
return err
2016-05-26 15:06:22 +00:00
}
2017-06-05 23:53:19 +00:00
func ( p * BabblerPlugin ) babbleSeedSuffix ( babblerName string , seed [ ] string ) ( string , error ) {
babbler , err := p . getBabbler ( babblerName )
if err != nil {
log . Print ( err )
return "" , nil
}
2017-06-07 00:03:07 +00:00
firstNode , curNode , err := p . verifyPhrase ( babbler , seed )
2017-06-05 23:53:19 +00:00
if err != nil {
log . Print ( err )
return "" , err
}
words := [ ] string { }
var curWord * BabblerWord
var shouldTerminate bool
curNode = firstNode
for {
curNode , curWord , shouldTerminate , err = p . getWeightedPreviousWord ( curNode )
if err != nil {
log . Print ( err )
return "" , err
}
if shouldTerminate {
break
}
words = append ( words , curWord . Word )
if len ( words ) >= 250 {
break
}
}
for i := 0 ; i < len ( words ) / 2 ; i ++ {
index := len ( words ) - ( i + 1 )
words [ i ] , words [ index ] = words [ index ] , words [ i ]
}
words = append ( words , seed ... )
return strings . TrimSpace ( strings . Join ( words , " " ) ) , nil
}
2017-06-07 00:03:07 +00:00
func ( p * BabblerPlugin ) getNextArcs ( babblerNodeId int64 ) ( [ ] * BabblerArc , error ) {
arcs := [ ] * BabblerArc { }
rows , err := p . db . Queryx ( ` select * from babblerArcs where fromNodeId = ?; ` , babblerNodeId )
if err != nil {
log . Print ( err )
return arcs , err
}
defer rows . Close ( )
for rows . Next ( ) {
var arc BabblerArc
err = rows . StructScan ( & arc )
if err != nil {
log . Print ( err )
return [ ] * BabblerArc { } , err
}
arcs = append ( arcs , & arc )
}
return arcs , nil
}
func ( p * BabblerPlugin ) getBabblerNodeById ( nodeId int64 ) ( * BabblerNode , error ) {
var node BabblerNode
err := p . db . QueryRowx ( ` select * from babblerNodes where id = ? LIMIT 1; ` , nodeId ) . StructScan ( & node )
if err != nil {
log . Print ( err )
return nil , err
}
return & node , nil
}
func shuffle ( a [ ] * BabblerArc ) {
for i := range a {
j := rand . Intn ( i + 1 )
a [ i ] , a [ j ] = a [ j ] , a [ i ]
}
}
func ( p * BabblerPlugin ) babbleSeedBookends ( babblerName string , start , end [ ] string ) ( string , error ) {
babbler , err := p . getBabbler ( babblerName )
if err != nil {
log . Print ( err )
return "" , nil
}
_ , startWordNode , err := p . verifyPhrase ( babbler , start )
if err != nil {
log . Print ( err )
return "" , err
}
endWordNode , _ , err := p . verifyPhrase ( babbler , end )
if err != nil {
log . Print ( err )
return "" , err
}
type searchNode struct {
babblerNodeId int64
previous * searchNode
}
open := [ ] * searchNode { & searchNode { startWordNode . NodeId , nil } }
closed := map [ int64 ] * searchNode { startWordNode . NodeId : open [ 0 ] }
goalNodeId := int64 ( - 1 )
2017-06-08 00:07:26 +00:00
for i := 0 ; i < len ( open ) && i < 1000 ; i ++ {
2017-06-07 00:03:07 +00:00
cur := open [ i ]
arcs , err := p . getNextArcs ( cur . babblerNodeId )
if err != nil {
return "" , err
}
//add a little randomization in through child ordering
shuffle ( arcs )
for _ , arc := range arcs {
if _ , ok := closed [ arc . ToNodeId ] ; ! ok {
child := & searchNode { arc . ToNodeId , cur }
open = append ( open , child )
closed [ arc . ToNodeId ] = child
if arc . ToNodeId == endWordNode . NodeId {
goalNodeId = cur . babblerNodeId
2017-06-08 00:07:26 +00:00
//add a little randomization in through maybe searching beyond this solution?
if rand . Intn ( 4 ) == 0 {
break
}
2017-06-07 00:03:07 +00:00
}
}
}
}
if goalNodeId == - 1 {
return "" , errors . New ( "couldn't find path" )
} else if closed [ goalNodeId ] . previous == nil {
seeds := append ( start , end ... )
return strings . Join ( seeds , " " ) , nil
}
words := [ ] string { }
curSearchNode := closed [ goalNodeId ]
for {
cur , err := p . getBabblerNodeById ( curSearchNode . babblerNodeId )
if err != nil {
log . Print ( err )
return "" , err
}
var w BabblerWord
err = p . db . QueryRowx ( ` select * from babblerWords where id = ? LIMIT 1; ` , cur . WordId ) . StructScan ( & w )
if err != nil {
log . Print ( err )
return "" , err
}
words = append ( words , w . Word )
curSearchNode = closed [ curSearchNode . previous . babblerNodeId ]
if curSearchNode . previous == nil {
break
}
}
for i := 0 ; i < len ( words ) / 2 ; i ++ {
index := len ( words ) - ( i + 1 )
words [ i ] , words [ index ] = words [ index ] , words [ i ]
}
words = append ( start , words ... )
words = append ( words , end ... )
return strings . Join ( words , " " ) , nil
}