diff --git a/.drone.yml b/.drone.yml index df3b1e5..6758f2c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,7 +4,6 @@ pipeline: group: build commands: - go get -v -d ./... - - sh build_plugins.sh - go build . test: image: golang @@ -13,8 +12,7 @@ pipeline: environment: [ test_api_token ] commands: - go get -v -d ./... - - sh build_plugins.sh - - go test . ./commands ./settings ./plugin_manager/ ./shared/ + - go test ./... publish: image: plugins/docker repo: klmp200/alfred diff --git a/.gitignore b/.gitignore index ee19125..eb5b34a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ settings_custom.json history.json -users.json \ No newline at end of file +users.json +chat_data.json \ No newline at end of file diff --git a/alfred.go b/alfred.go index 1edaa8a..b8d4956 100644 --- a/alfred.go +++ b/alfred.go @@ -2,14 +2,13 @@ * @Author: Bartuccio Antoine * @Date: 2018-07-23 15:24:22 * @Last Modified by: klmp200 -* @Last Modified time: 2018-07-25 14:31:13 +* @Last Modified time: 2018-07-27 16:13:32 */ package main import ( "./commands" - "./plugin_manager" "./settings" "./shared" tb "gopkg.in/tucnak/telebot.v2" @@ -19,7 +18,7 @@ import ( func main() { registered_commands := map[string]func(*tb.Message){ - // tb.OnText: commands.OnText, + tb.OnText: commands.OnText, "/hello": commands.Hello, "/sponge": commands.Sponge, "/git": commands.Git, @@ -27,6 +26,9 @@ func main() { "/setgender": commands.SetGender, "/gender": commands.Gender, "/roll": commands.Dice, + "/trump": commands.LastTrumpTweet, + "/trends": commands.TwitterTrends, + "/chaos": commands.TwitterSJW, } if err := settings.LoadSettings("settings.json", "settings_custom.json"); err != nil { @@ -38,6 +40,8 @@ func main() { settings.Settings["history file"].(string)) log.Println("Initialize users infos") shared.InitUsers(settings.Settings["users file"].(string)) + log.Println("Initialize chat data") + shared.InitChatData(settings.Settings["chat data file"].(string)) log.Println("Bot initialisation") b, err := tb.NewBot(tb.Settings{ @@ -54,9 +58,6 @@ func main() { b.Handle(key, value) } - plugin_manager.Init("plugin", b) - b.Handle(tb.OnText, plugin_manager.HandleMessage) - plugin_manager.StartPlugins() log.Println("Starting bot") b.Start() } diff --git a/build_plugins.sh b/build_plugins.sh deleted file mode 100755 index cfc47b8..0000000 --- a/build_plugins.sh +++ /dev/null @@ -1,12 +0,0 @@ -# @Author: Bartuccio Antoine -# @Date: 2018-07-25 12:47:23 -# @Last Modified by: klmp200 -# @Last Modified time: 2018-07-25 12:58:04 -#!/bin/sh -cd plugin -rm -f *.so -FILES=`ls *.go` -for FILE in $FILES -do - go build -buildmode=plugin $FILE -done \ No newline at end of file diff --git a/commands/gender.go b/commands/gender.go index b126dbf..26d1d80 100644 --- a/commands/gender.go +++ b/commands/gender.go @@ -2,7 +2,7 @@ * @Author: Bartuccio Antoine * @Date: 2018-07-24 14:55:33 * @Last Modified by: klmp200 -* @Last Modified time: 2018-07-24 20:29:36 +* @Last Modified time: 2018-07-25 01:29:53 */ package commands @@ -24,6 +24,10 @@ func SetGender(m *tb.Message) { return } data := strings.Join(split, " ") + if data == "" { + shared.Bot.Send(m.Chat, "Attention, votre genre est vide. Ce n'est pas enregistrable.") + return + } shared.Users.Set(m.Sender.Username, "gender", data) shared.Bot.Send(m.Chat, "Votre genre est enregistré, je vous considère maintenant comme « "+data+" ».") } @@ -52,13 +56,12 @@ func Gender(m *tb.Message) { func cleanGender(slice []string) []string { for i := range slice { + slice[i] = strings.Replace(slice[i], "\\", "", -1) + slice[i] = strings.Replace(slice[i], "@", "", -1) clean := false for !clean { clean = true - if strings.HasPrefix(slice[i], "@") { - slice[i] = strings.Replace(slice[i], "@", "", 1) - clean = false - } else if strings.HasPrefix(slice[i], "/") { + if strings.HasPrefix(slice[i], "/") { slice[i] = strings.Replace(slice[i], "/", "", 1) clean = false } diff --git a/commands/twitter.go b/commands/twitter.go new file mode 100644 index 0000000..42e9ad8 --- /dev/null +++ b/commands/twitter.go @@ -0,0 +1,114 @@ +/* +* @Author: Bartuccio Antoine +* @Date: 2018-07-25 18:51:38 +* @Last Modified by: klmp200 +* @Last Modified time: 2018-07-27 16:49:59 + */ + +package commands + +import ( + "../settings" + "../shared" + "github.com/dghubble/go-twitter/twitter" + "github.com/dghubble/oauth1" + tb "gopkg.in/tucnak/telebot.v2" + "strconv" + "strings" + "time" +) + +var client *twitter.Client + +func initTwitter() { + config := oauth1.NewConfig( + settings.Settings["twitter consumer key"].(string), + settings.Settings["twitter consumer secret"].(string), + ) + token := oauth1.NewToken( + settings.Settings["twitter access token"].(string), + settings.Settings["twitter access secret"].(string), + ) + + http_client := config.Client(oauth1.NoContext, token) + client = twitter.NewClient(http_client) +} + +func testOrInitTwitter() { + if client == nil { + initTwitter() + } +} + +func twitterCommunicationError(m *tb.Message) { + shared.Bot.Send(m.Chat, "Désolé, les serveurs de twitter sont injoignables.") +} + +func LastTrumpTweet(m *tb.Message) { + testOrInitTwitter() + user, _, err := client.Users.Show(&twitter.UserShowParams{ScreenName: "realDonaldTrump"}) + if err != nil { + twitterCommunicationError(m) + return + } + timeline, _, err := client.Timelines.UserTimeline(&twitter.UserTimelineParams{ScreenName: "realDonaldTrump"}) + if err != nil { + twitterCommunicationError(m) + return + } + response := []string{ + user.Name, + "\nFollowers : ", + strconv.Itoa(user.FollowersCount), + "\nStatus : ", + user.Description, + "\n ---", + "\n" + timeline[0].Text, + "\n ---", + "\n" + timeline[0].Source, + } + shared.Bot.Send(m.Chat, strings.Join(response, " ")) +} + +func TwitterTrends(m *tb.Message) { + testOrInitTwitter() + trends, _, err := client.Trends.Place(int64(615702), nil) + if err != nil { + twitterCommunicationError(m) + return + } + message := "Voici les dernières tendances en France" + for _, trend := range trends[0].Trends { + message += "\n" + trend.Name + } + shared.Bot.Send(m.Chat, message) +} + +func TwitterSJW(m *tb.Message) { + testOrInitTwitter() + last_use, exists := shared.ChatData.Get(m.Chat.ID, "last chaos use") + if exists { + var date time.Time + if _, is_string := last_use.(string); is_string { + date, _ = time.Parse(time.RFC3339, last_use.(string)) + } else { + date = last_use.(time.Time) + } + if time.Now().Before(date.Add(time.Hour * 24)) { + shared.Bot.Send(m.Chat, "Arioch ne répondra pas à votre appel.") + return + } + } + shared.ChatData.Set(m.Chat.ID, "last chaos use", time.Now()) + + tweets, _, err := client.Search.Tweets(&twitter.SearchTweetParams{ + Query: "#SJW", + }) + if err != nil { + twitterCommunicationError(m) + return + } + for _, tweet := range tweets.Statuses { + shared.Bot.Send(m.Chat, tweet.Text) + } +} diff --git a/plugin/plugin.go b/plugin/plugin.go deleted file mode 100644 index 14c2637..0000000 --- a/plugin/plugin.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "../plugin_manager" - tb "gopkg.in/tucnak/telebot.v2" -) - -type plugin string - -func (g plugin) GetCommands() []string { - return []string{"plugin"} -} - -func (g plugin) HandleCommand(bot *tb.Bot, msg *tb.Message, cmd string, args []string) { - if cmd == "plugin" { - ok := false - if len(args) >= 1 { - if args[0] == "list" { - lst := "" - for _, pName := range plugin_manager.GetPluginList() { - lst = lst + "-" + pName + " (status: " - if plugin_manager.IsPluginEnable(pName) { - lst += "enable, " - } else { - lst += "disable, " - } - if plugin_manager.IsPluginRunning(pName) { - lst += "running" - } else { - lst += "stopped" - } - lst += ")\n" - } - bot.Send(msg.Chat, "liste des plugins disponible:\n"+lst) - ok = true - } - if args[0] == "enable" && len(args) >= 2 { - for _, name := range args[1:] { - if plugin_manager.ExistPlugin(name) { - plugin_manager.EnablePlugin(name, true) - bot.Send(msg.Chat, "enable plugin "+name) - } - } - ok = true - } - if args[0] == "disable" && len(args) >= 2 { - for _, name := range args[1:] { - if plugin_manager.ExistPlugin(name) { - plugin_manager.EnablePlugin(name, false) - bot.Send(msg.Chat, "disable plugin "+name) - } - } - ok = true - } - } - if !ok { - bot.Send(msg.Chat, "command inconnue\n"+ - "liste de plugin:\n"+ - "/plugin list\n"+ - "activer un/des plugins\n"+ - "/plugin enable nom_plugin\n"+ - "désactiver un/des plugins\n"+ - "/plugin disable nom_plugin") - } - } -} - -var Plugin plugin diff --git a/plugin/test.go b/plugin/test.go deleted file mode 100644 index 6fc0446..0000000 --- a/plugin/test.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - tb "gopkg.in/tucnak/telebot.v2" - "log" -) - -type plugin string - -func (g plugin) GetCommands() []string { - return []string{"ping", "test"} -} - -func (g plugin) Load() bool { - log.Println("plugin test loaded!") - return true -} - -func (g plugin) HandleMessage(bot *tb.Bot, msg *tb.Message) { - log.Println("plugin test message: " + msg.Text) -} - -func (g plugin) HandleCommand(bot *tb.Bot, msg *tb.Message, cmd string, args []string) { - if cmd == "ping" { - bot.Send(msg.Chat, "pong!") - } - argsS := "" - for _, arg := range args { - argsS = argsS + " " + arg - } - log.Print("plugin test cmd: " + cmd + " (args:" + argsS + ")") - -} - -func (g plugin) Unload() bool { - log.Println("plugin test unloaded!") - return true -} - -var Plugin plugin diff --git a/plugin/test2.go b/plugin/test2.go deleted file mode 100644 index 9883f11..0000000 --- a/plugin/test2.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - tb "gopkg.in/tucnak/telebot.v2" - "log" -) - -type plugin string - -func (g plugin) Load() { - log.Println("plugin test2 loaded!") -} - -func (g plugin) HandleMessage(bot *tb.Bot, msg string) { - log.Println("test2 message: " + msg) -} - -func (g plugin) Unload() { - log.Println("plugin test2 unloaded!") -} - -var Plugin plugin diff --git a/plugin_manager/manager.go b/plugin_manager/manager.go deleted file mode 100644 index c76dda8..0000000 --- a/plugin_manager/manager.go +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @Author: KLIPFEL Arthur - * @Date: 2018-08-24 12:17:17 - */ -package plugin_manager - -import ( - tb "gopkg.in/tucnak/telebot.v2" - "log" - "path/filepath" - "strings" - "sync" -) - -type PluginCtrl struct { - plugin Plugin - mux sync.Mutex - running bool - enable bool -} - -var pluginDir string -var pluginsRunning bool -var plugins map[string]PluginCtrl -var context *tb.Bot - -func Init(_pluginDir string, bot *tb.Bot) { - pluginDir = _pluginDir - pluginsRunning = false - plugins = make(map[string]PluginCtrl) - context = bot - for _, fileName := range GetSoFiles(pluginDir) { - var p PluginCtrl - p.plugin = LoadSoFile(filepath.Join(pluginDir, fileName)) - if p.plugin != nil { - p.running = false - p.enable = true - plugins[fileName[:len(fileName)-3]] = p - } - } -} - -func GetPluginList() []string { - var lst []string - for name, _ := range plugins { - lst = append(lst, name) - } - return lst -} - -func IsPluginRunning(name string) bool { - if p, ok := plugins[name]; ok { - return p.running - } - return false -} - -func ExistPlugin(name string) bool { - if _, ok := plugins[name]; ok { - return true - } - return false -} - -func GetPlugin(name string) Plugin { - if p, ok := plugins[name]; ok { - return p - } - return nil -} - -func IsPluginEnable(name string) bool { - if p, ok := plugins[name]; ok { - return p.enable - } - return false -} - -func EnablePlugin(name string, enable bool) { - if p, ok := plugins[name]; ok { - if enable != p.enable { - p.enable = enable - plugins[name] = p - if pluginsRunning { - if enable { - if !p.running { - startPlugin(name) - } - } else { - if p.running { - stopPlugin(name) - } - } - } - } - } else { - log.Fatal("error: plugin " + name + " not founded") - } -} - -func StopPlugins() { - for k, _ := range plugins { - stopPlugin(k) - } - pluginsRunning = false -} - -func HandleMessage(msg *tb.Message) { - for _, val := range plugins { - if val.enable && val.running { - if strings.HasPrefix(msg.Text, "/") { - split := strings.Split(msg.Text, " ") - split[0] = split[0][1:] - if Contains(split[0], ExecGetCommands(val.plugin)) { - ExecHandleCommand(val.plugin, context, msg, split[0], split[1:]) - } - } else { - ExecHandleMessage(val.plugin, context, msg) - } - } - } -} - -func StartPlugins() { - for k, val := range plugins { - if val.enable { - startPlugin(k) - } - } - pluginsRunning = true -} - -func startPlugin(name string) { - if p, ok := plugins[name]; ok { - //p.mux.Lock() - if !p.running && p.enable { - p.running = ExecLoad(p.plugin) - plugins[name] = p - } - //p.mux.Unlock() - } -} - -func stopPlugin(name string) { - if p, ok := plugins[name]; ok { - //p.mux.Lock() - if p.running { - p.running = false - plugins[name] = p - ExecUnload(p.plugin) - } - //p.mux.Unlock() - } -} - -func Exit() { - for _, p := range plugins { - if p.running { - ExecUnload(p.plugin) - } - } -} diff --git a/plugin_manager/tools.go b/plugin_manager/tools.go deleted file mode 100644 index 3e15f72..0000000 --- a/plugin_manager/tools.go +++ /dev/null @@ -1,127 +0,0 @@ -/** - * @Author: KLIPFEL Arthur - * @Date: 2018-08-24 12:17:17 - */ -package plugin_manager - -import ( - tb "gopkg.in/tucnak/telebot.v2" - "io/ioutil" - "log" - "plugin" - "strings" -) - -type TestGetCommands interface { - GetCommands() []string -} - -type TestLoad interface { - Load() bool -} - -type TestHandleMessage interface { - HandleMessage(bot *tb.Bot, msg *tb.Message) -} - -type TestHandleCommand interface { - HandleCommand(bot *tb.Bot, msg *tb.Message, cmd string, args []string) -} - -type TestUnload interface { - Unload() bool -} - -type Plugin interface { /* - GetCommandes() []string - Load() bool - HandleMessage(bot *tb.Bot, msg *tb.Message) - HandleCommand(bot *tb.Bot, msg *tb.Message, cmd string, args []string) - Unload() bool*/ -} - -func GetSoFiles(dir string) []string { - var slice []string - - files, err := ioutil.ReadDir(dir) - if err != nil { - log.Fatal(err) - } else { - for _, f := range files { - if strings.HasSuffix(f.Name(), ".so") { - slice = append(slice, f.Name()) - } - } - } - return slice -} - -func LoadSoFile(file string) Plugin { - plug, err := plugin.Open(file) - if err != nil { - log.Fatal(err) - return nil - } - - symPlugin, err := plug.Lookup("Plugin") - if err != nil { - log.Fatal(err) - return nil - } - - var plugin Plugin - //plugin, _ = symPlugin.(Plugin) - plugin, ok := symPlugin.(Plugin) - if !ok { - log.Fatal(file + ": unexpected type from module symbol") - return nil - } - return plugin -} - -func ExecGetCommands(plugin Plugin) []string { - p, ok := plugin.(TestGetCommands) - if ok { - return p.GetCommands() - } - return []string{} -} - -func ExecLoad(plugin Plugin) bool { - p, ok := plugin.(TestLoad) - if ok { - return p.Load() - } - return true -} - -func ExecHandleMessage(plugin Plugin, bot *tb.Bot, msg *tb.Message) { - p, ok := plugin.(TestHandleMessage) - if ok { - p.HandleMessage(bot, msg) - } -} - -func ExecHandleCommand(plugin Plugin, bot *tb.Bot, msg *tb.Message, cmd string, args []string) { - p, ok := plugin.(TestHandleCommand) - if ok { - p.HandleCommand(bot, msg, cmd, args) - } -} - -func ExecUnload(plugin Plugin) bool { - p, ok := plugin.(TestUnload) - if ok { - return p.Unload() - } - return true -} - -func Contains(e string, s []string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} diff --git a/settings.json b/settings.json index 6c494ad..de9860a 100644 --- a/settings.json +++ b/settings.json @@ -1,6 +1,11 @@ { "token": "INSERT TOKEN HERE", + "twitter access token": "INSERT TOKEN HERE", + "twitter access secret": "INSERT TOKEN HERE", + "twitter consumer key": "INSERT TOKEN HERE", + "twitter consumer secret": "INSERT TOKEN HERE", "history size": 10, "history file": "history.json", - "users file": "users.json" -} + "users file": "users.json", + "chat data file": "chat_data.json" +} \ No newline at end of file diff --git a/shared/chat.go b/shared/chat.go new file mode 100644 index 0000000..6158a28 --- /dev/null +++ b/shared/chat.go @@ -0,0 +1,82 @@ +/* +* @Author: Bartuccio Antoine +* @Date: 2018-07-27 15:37:59 +* @Last Modified by: klmp200 +* @Last Modified time: 2018-07-27 16:06:51 + */ + +package shared + +import ( + "encoding/json" + "io/ioutil" + "sync" +) + +// General purpose chat info storage +type chatData struct { + mutex sync.Mutex + data map[int64]map[string]interface{} +} + +type chatDataFile struct { + mutex sync.Mutex + path string +} + +var ChatData chatData +var cdf chatDataFile + +// Init chat data meant to store infos about a chat. +func InitChatData(path string) { + cdf = chatDataFile{path: path} + ChatData = chatData{data: make(map[int64]map[string]interface{})} + ChatData.mutex.Lock() + defer ChatData.mutex.Unlock() + cdf.read() + +} + +func (c chatData) Set(chat int64, key string, data interface{}) { + c.mutex.Lock() + defer c.mutex.Unlock() + if _, exists := c.data[chat]; !exists { + c.data[chat] = make(map[string]interface{}) + } + c.data[chat][key] = data + go cdf.write() +} + +func (c chatData) Get(chat int64, key string) (interface{}, bool) { + c.mutex.Lock() + defer c.mutex.Unlock() + m, exists := c.data[chat] + if !exists { + return nil, false + } + data, ok := m[key] + if !ok { + return nil, false + } + return data, true +} + +func (c chatDataFile) read() { + c.mutex.Lock() + defer c.mutex.Unlock() + data, err := ioutil.ReadFile(c.path) + if err != nil { + // File doesn't exist, skip import + return + } + json.Unmarshal(data, &ChatData.data) +} + +func (c chatDataFile) write() { + c.mutex.Lock() + defer c.mutex.Unlock() + ChatData.mutex.Lock() + defer ChatData.mutex.Unlock() + data, _ := json.Marshal(ChatData.data) + ioutil.WriteFile(c.path, data, 0770) +}