diff --git a/kernel/cache/cache.go b/kernel/cache/cache.go index e60676c..1fc9ff1 100644 --- a/kernel/cache/cache.go +++ b/kernel/cache/cache.go @@ -6,6 +6,7 @@ import ( "main/kernel/cache/cachetypes" ) +//Cache for RSS data type RssCaches struct { News cachetypes.RssCache Bash cachetypes.RssCache @@ -13,28 +14,24 @@ type RssCaches struct { Zadolbali cachetypes.RssCache } +//Cache for functions data type CommandDataCaches struct { Help cachetypes.HelpCache Cities cachetypes.CitiesCache } +//Main cache type DataCache struct { RssCache RssCaches CommandDataCache CommandDataCaches DictionaryCache cachetypes.DictCache } +//Cache initialization func (cache *DataCache) InitCache() { cache.DictionaryCache.Data = *aiml.NewAIML() cache.CommandDataCache.Help.InitCache(conf.CommandsDirPath + "/help.xml") cache.CommandDataCache.Cities.InitCache(conf.CommandsDirPath + "/cities.xml") cache.DictionaryCache.UpdateCache(conf.DataDirPath + "/dict.aiml.xml") -} - -func (cache *RssCaches) UpdateRssCache(newCache map[string][]string) { - cache.News.Data = newCache["news"] - cache.Bash.Data = newCache["bash"] - cache.IThappens.Data = newCache["ithappens"] - cache.Zadolbali.Data = newCache["zadolbali"] } \ No newline at end of file diff --git a/kernel/cache/cachetypes/cities.go b/kernel/cache/cachetypes/cities.go index 2da0f6b..68404eb 100644 --- a/kernel/cache/cachetypes/cities.go +++ b/kernel/cache/cachetypes/cities.go @@ -9,28 +9,33 @@ import ( type City struct { XMLName xml.Name `xml:"city"` - Name string `xml:"name,attr"` + Name string `xml:"name,attr"` //Name of the city } type XMLCities struct { XMLName xml.Name `xml:"xml"` - CitiesList []City `xml:"data>city"` + CitiesList []City `xml:"data>city"` //Cities array } +//Cache for the cities game type CitiesCache struct { sync.Mutex - path string - Data XMLCities + path string //Path to file with XML data that will be parsed + Data XMLCities //Cache } +//Cities cache initialization func (citiesCache *CitiesCache) InitCache(path string) { + //Reading XML data text, err := ioutil.ReadFile(path) if err != nil { log.Println("[ERROR] [main::kernel::mapCache::cachetypes::cachetypes.go::CitiesCache.InitCache] Failed to read file: ", err) } - citiesCache.Lock() + citiesCache.Lock() //Cache locking defer citiesCache.Unlock() + + //Parsing XML data if err := xml.Unmarshal(text, &citiesCache.Data); err != nil { log.Println("[ERROR] [main::kernel::mapCache::cachetypes::cachetypes.go::CitiesCache.InitCache] Failed to unmarshal data: ", err) } diff --git a/kernel/cache/cachetypes/dict.go b/kernel/cache/cachetypes/dict.go index 1e3ba5f..a35c2c6 100644 --- a/kernel/cache/cachetypes/dict.go +++ b/kernel/cache/cachetypes/dict.go @@ -6,15 +6,18 @@ import ( "log" ) +//Cache for the dictionary for the bot type DictCache struct { sync.Mutex - Data aiml.AIML + Data aiml.AIML //Cache data } func (dictCache *DictCache) UpdateCache(path string) { - dictCache.Lock() + dictCache.Lock() //Cache locking + defer dictCache.Unlock() + + //Learning AIML database if err := dictCache.Data.Learn(path); err != nil { log.Println("[ERROR] [main::kernel::mapCache::mapCache.go::DictCache.InitCache()] Failed to update mapCache ", err) } - dictCache.Unlock() } diff --git a/kernel/cache/cachetypes/help.go b/kernel/cache/cachetypes/help.go index 7912111..d989020 100644 --- a/kernel/cache/cachetypes/help.go +++ b/kernel/cache/cachetypes/help.go @@ -9,36 +9,40 @@ import ( type Help struct { XMLName xml.Name `xml:"category"` - Name string `xml:"name,attr"` - Title string `xml:"title"` - State string `xml:"state"` - Description string `xml:"description"` + Name string `xml:"name,attr"` //Command name + Title string `xml:"title"` //Help title + State string `xml:"state"` //Command state + Description string `xml:"description"` //Command description Samples []struct { XMLName xml.Name `xml:"sample"` - Body string `xml:"body"` - Out string `xml:"out"` + Body string `xml:"body"` //Sample input + Out string `xml:"out"` //Sample output } `xml:"samples>sample"` } type XMLHelp struct { xml.Name `xml:"xml"` - HelpList []Help `xml:"help>category"` //TODO FIX + HelpList []Help `xml:"help>category"` //Help writings array } +//Cache for the help command type HelpCache struct { sync.Mutex - path string - Data XMLHelp + path string //Path to file with XML data that will be parsed + Data XMLHelp //Cache } func (helpCache *HelpCache) InitCache(path string) { + //Reading XML text, err := ioutil.ReadFile(path) if err != nil { log.Println("[ERROR] [main::kernel::mapCache::cachetypes::cachetypes.go::HelpCache.InitCache] Failed to read file: ", err) } - helpCache.Lock() + helpCache.Lock() //Cache locking defer helpCache.Unlock() + + //Parsing data if err := xml.Unmarshal(text, &helpCache.Data); err != nil { log.Println("[ERROR] [main::kernel::mapCache::cachetypes::cachetypes.go::HelpCache.InitCache] Failed to unmarshal data: ", err) } diff --git a/kernel/cache/cachetypes/rss.go b/kernel/cache/cachetypes/rss.go index 0a705a1..e40edf4 100644 --- a/kernel/cache/cachetypes/rss.go +++ b/kernel/cache/cachetypes/rss.go @@ -7,9 +7,10 @@ import ( type RssCache struct { sync.Mutex - Data []string + Data []string //Writing array } +//Choose random writing func (rssCache *RssCache) ChooseRandom() string { return rssCache.Data[rand.Intn(len(rssCache.Data) - 1)] } \ No newline at end of file diff --git a/kernel/commands/commands.go b/kernel/commands/commands.go index 46c8d34..6cd08fa 100644 --- a/kernel/commands/commands.go +++ b/kernel/commands/commands.go @@ -1,25 +1,16 @@ package commands import ( - "time" - "math/rand" - "sort" - "strconv" "strings" - "fmt" "main/kernel/commands/tools" - "main/web/vk" - "main/kernel/cache" - "main/kernel/interception" + "strconv" + "sort" + "time" + "fmt" + "math/rand" + "main/kernel/performer/functions" ) -type FuncArgs struct { - ApiChan vk.ChanKit - Message vk.Message - DataCache cache.DataCache - InterceptIndications interception.Indications -} - const ( //GET_STATE getStateAnswer = "Я отсортировала массив из 1000 элементов за %v наносекунд" @@ -42,7 +33,7 @@ const ( incorrectSymbolError = `Ты должен назвать слово на букву "%v"` ) -func GetState(args FuncArgs) { +func GetState(args functions.FuncArgs) { start := time.Now().UnixNano() sort.Ints(rand.Perm(1000)) metering := strconv.FormatInt(time.Now().UnixNano() - start, 10) @@ -52,7 +43,7 @@ func GetState(args FuncArgs) { }) } -func GetGen(args FuncArgs) { +func GetGen(args functions.FuncArgs) { var message string information := strconv.FormatInt(int64(tools.GetRandomNum(args.Message.Text)%100), 10) message = fmt.Sprintf(getGenAnswer, information) @@ -62,7 +53,7 @@ func GetGen(args FuncArgs) { }) } -func GetHelp(args FuncArgs) { +func GetHelp(args functions.FuncArgs) { var message string if len(args.Message.Text) != 1 { message = tools.GetHelp(args.Message.Text, args.DataCache.CommandDataCache.Help) @@ -75,28 +66,28 @@ func GetHelp(args FuncArgs) { }) } -func Bash(args FuncArgs) { +func Bash(args functions.FuncArgs) { args.ApiChan.MakeRequest("messages.send", map[string]string{ "user_id": strconv.FormatInt(args.Message.UserId, 10), "message": args.DataCache.RssCache.Bash.ChooseRandom(), }) } -func IThappens(args FuncArgs) { +func IThappens(args functions.FuncArgs) { args.ApiChan.MakeRequest("messages.send", map[string]string{ "user_id": strconv.FormatInt(args.Message.UserId, 10), "message": args.DataCache.RssCache.IThappens.ChooseRandom(), }) } -func Zadolbali(args FuncArgs) { +func Zadolbali(args functions.FuncArgs) { args.ApiChan.MakeRequest("messages.send", map[string]string{ "user_id": strconv.FormatInt(args.Message.UserId, 10), "message": args.DataCache.RssCache.Zadolbali.ChooseRandom(), }) } -func News(args FuncArgs) { +func News(args functions.FuncArgs) { words := strings.Split(args.Message.Text, " ") var message string if tools.IfMatch(words, []string{"i"}) { @@ -116,7 +107,7 @@ func News(args FuncArgs) { }) } -func Cities(args FuncArgs) { +func Cities(args functions.FuncArgs) { args.InterceptIndications.Add(args.Message.UserId) args.ApiChan.MakeRequest("messages.send", map[string]string{ "user_id": strconv.FormatInt(args.Message.UserId, 10), @@ -187,10 +178,10 @@ func Cities(args FuncArgs) { answer = winMessage - SEND: args.ApiChan.MakeRequest("messages.send", map[string]string{ - "user_id": strconv.FormatInt(args.Message.UserId, 10), - "message": answer, - }) + SEND: args.ApiChan.MakeRequest("messages.send", map[string]string{ + "user_id": strconv.FormatInt(args.Message.UserId, 10), + "message": answer, + }) args.DataCache.CommandDataCache.Cities.Unlock() } } \ No newline at end of file diff --git a/kernel/commands/tools/tools.go b/kernel/commands/tools/tools.go index a23893a..415ba3a 100644 --- a/kernel/commands/tools/tools.go +++ b/kernel/commands/tools/tools.go @@ -28,10 +28,10 @@ const ( ) var emojiDict = map[string]string{ - "ready": "📗", - "test": "📙", - "error": "📕", - "dev": "📘", + "ready": "📗", //Command is ready + "test": "📙", //Command is testing + "error": "📕", //Command not working + "dev": "📘", //Command in development } @@ -53,15 +53,15 @@ func IfMatch(args []string, template []string) bool { l: for index, word := range args { if index < len(template) { switch template[index] { - case IntegerTag: + case IntegerTag: //Must be integer _, err := strconv.ParseInt(word, 10, 64) if err != nil { return false } + case AnythingTag: //Anything + break l case StringTag: //TODO ADD - case AnythingTag: - break l } } diff --git a/kernel/performer/functions/commands.go b/kernel/performer/functions/commands.go new file mode 100644 index 0000000..ccba016 --- /dev/null +++ b/kernel/performer/functions/commands.go @@ -0,0 +1,33 @@ +package functions + +import ( + "main/web/vk" + "main/kernel/cache" + "main/kernel/interception" + "strconv" +) + +const messageSendMethod = "messages.send" + +//args for bot commands +type FuncArgs struct { + ApiChan vk.ChanKit //ChanKit for safety API requests + Message vk.Message //Message from user + DataCache cache.DataCache //Cache of data + InterceptIndications interception.Indications //Interception tools +} + +//Send message back to the user +func (args *FuncArgs) Reply(message string, attach ...string) { + var attachments string + + if len(attachments) != 0 { + attachments = attach[0] + } + + args.ApiChan.MakeRequest(messageSendMethod, map[string]string{ + "user_id": strconv.FormatInt(args.Message.UserId, 10), + "message": message, + "attachments": attachments, + }) +} \ No newline at end of file diff --git a/kernel/performer.go b/kernel/performer/performer.go similarity index 60% rename from kernel/performer.go rename to kernel/performer/performer.go index 934678a..22a04dd 100644 --- a/kernel/performer.go +++ b/kernel/performer/performer.go @@ -1,11 +1,11 @@ -package kernel +package performer import ( - "strconv" "strings" - "main/kernel/commands" + "main/kernel/performer/functions" "log" "fmt" + "main/kernel/commands" ) const ( @@ -16,17 +16,19 @@ const ( internalErrorMessage = "Internal error" ) -var commandsList = map[string]func(args commands.FuncArgs) { - "state": commands.GetState, - "help": commands.GetHelp, - "cities": commands.Cities, + +var CommandsList = map[string]func(args functions.FuncArgs) { + "state": commands.GetState, + "help": commands.GetHelp, + "cities": commands.Cities, "information": commands.GetGen, - "bash": commands.Bash, - "ithappens": commands.IThappens, - "zadolbali": commands.Zadolbali, - "news": commands.News, + "bash": commands.Bash, + "ithappens": commands.IThappens, + "zadolbali": commands.Zadolbali, + "news": commands.News, } +//This function find flags in the text func findFlags(text string, flags []string) (map[string]string, string) { out := make(map[string]string) minFlagindex := len(text) @@ -46,45 +48,47 @@ func findFlags(text string, flags []string) (map[string]string, string) { return out, text[:minFlagindex] } -func checkInterceptIndications(args commands.FuncArgs) bool { +//This function checks if need to doing interception for this message +func checkInterceptIndications(args functions.FuncArgs) bool { args.InterceptIndications.Lock() defer args.InterceptIndications.Unlock() if args.InterceptIndications.InterceptedMessage[args.Message.UserId] != nil { - args.InterceptIndications.InterceptedMessage[args.Message.UserId] <- args.Message return true } return false } -func Perform(args commands.FuncArgs) { - if checkInterceptIndications(args) { return } +//Main performing function +func Perform(args functions.FuncArgs) { + if checkInterceptIndications(args) { + args.InterceptIndications.InterceptedMessage[args.Message.UserId] <- args.Message + return + } + text := strings.ToLower(strings.Trim(args.Message.Text, inputCutset)) - log.Println("[INFO] No command detected. Running performation") args.DataCache.DictionaryCache.Lock() - answer, err := args.DataCache.DictionaryCache.Data.Respond(strings.ToLower(text)) + answer, err := args.DataCache.DictionaryCache.Data.Respond(strings.ToLower(text)) //Gets answer args.DataCache.DictionaryCache.Unlock() if err != nil { log.Println("[ERROR] [main::kernel::performer.go] Failed to get answer: ", err) } - params := map[string]string{ - "user_id": strconv.FormatInt(args.Message.UserId, 10), - } - flags, newMessage := findFlags(answer, []string{attachFlag, callFlag}) - params["message"] = newMessage - params["attachment"] = flags["attach"] + flags, message := findFlags(answer, []string{attachFlag, callFlag}) //Checks flags + //Checks command call if flags["call"] != "" { + //Gets function params funcParams := strings.Split(flags[callFlag], callSep) name := funcParams[0] - if commandsList[name] != nil { + //Runs command + if CommandsList[name] != nil { args.Message.Text = funcParams[1] - commandsList[name](args) + CommandsList[name](args) return } else { - params["message"] = internalErrorMessage + message = internalErrorMessage } } - args.ApiChan.MakeRequest("messages.send", params) + args.Reply(message, flags["attach"]) //Sends answer to the user } \ No newline at end of file diff --git a/main.go b/main.go index 6a4701a..fcae82e 100644 --- a/main.go +++ b/main.go @@ -7,11 +7,11 @@ import ("os" "main/conf" "main/web/vk" "main/web/rss" - "main/kernel" "main/kernel/cache" "main/kernel/interception" - "main/kernel/commands" "strconv" + "main/kernel/performer/functions" + "main/kernel/performer" ) //Is log will be written @@ -30,9 +30,9 @@ var ( {conf.DataDirPath + "/dict.aiml.xml", false}, // data/dict.aiml.xml {conf.LogDirPath, true}, // data/log {logFilePath, false}, // data/log/xxxxxxxxxx.log - {conf.CommandsDirPath, true}, // data/commands - {conf.CommandsDirPath + "/help.xml", false}, // data/commands/help.xml - {conf.CommandsDirPath + "/cities.xml", false}, // data/commands/cities.xml + {conf.CommandsDirPath, true}, // data/functions + {conf.CommandsDirPath + "/help.xml", false}, // data/functions/help.xml + {conf.CommandsDirPath + "/cities.xml", false}, // data/functions/cities.xml } ) @@ -89,11 +89,11 @@ func main() { select { case message := <- lp.NewMessageChan: //New message log.Println("[INFO] New message detected: ", message) - args := commands.FuncArgs{ + args := functions.FuncArgs{ ApiChan: api.ChanKit, Message: message, DataCache: dataCache, InterceptIndications: indications, } //Creating func params - go kernel.Perform(args) + go performer.Perform(args) case request := <- api.ChanKit.RequestChan: //New api request out, err := api.Request(request.Name, request.Params) //Request API method api.ChanKit.AnswerChan <- vk.Answer{out, err} //Sending API answer back