commit 4fec8f0fe77f15d37b6f2eaa553ba9e223268dd0 Author: Vladimir Hodakov Date: Wed Oct 4 17:56:18 2017 +0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d3ed4c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.yml diff --git a/SPECS b/SPECS new file mode 100644 index 0000000..94781d3 --- /dev/null +++ b/SPECS @@ -0,0 +1,82 @@ +#### Чо мы, собстна, уметь должны + +1. Велкам-бот. Приветствовать новых юзеров в чате по алгоритму: + * Если у бота нет профиля игрока, давать сообщение 1, где предлагать кинуться в него профилем + * Если у бота есть профиль игрока: + * Если это Инстинкт, выдается сообщение 2 + * Если другая лига, выдается сообщение 3 + * Если профиля игрока нет, оповещать в спец-канал, что в другой чат зашел человек без профиля (новичок). + * Если профиль есть, и он не Инстинкт, давать сообщение в спец-канал об этом. + +2. Хранение и использование инфы. + * Профиль хранится в базе. Стирать старые версии мы, конечно же, не будем. + * С профиля берем: + * Ник + * Голда + * Опыт + * Покемемы на руках + * Уровень + * Все, что забирает бот синих + * Отчеты о битвах. Привязываются к юзеру, с него парсится: + * Время атаки + * Номер отчета + * Цель + * Опыт + * Дельта денег (заработано или проебано) + +3. Хелп + * Многоуровневый! + +4. /exp_day – выводим опыт юзера за сутки +5. /exp_week – экспа за неделю с понедельника 01-00 +6. Писькомерки: + * Опыт за неделю/месяц/день + * Опыт с битв за неделю/месяц/день + +### Для привелегированных + +7. Постинг покемема. Пока в базу, куда все статы закидываем. +8. /pin %text% – запинить текст во все чаты с ботом +9. /pin 1,2,3,5,10 – запинить текст в чаты с номерами +10. /msg_all – широковещательное сообщение всем в боте, у кого профиль Инстинкта + + +#### Данные юзеров, ням-ням! + +* Ник в телеграме +* Ник в игре +* Лига +* Левел +* Экспа +* Атака без б +* Атака с б +* Телеграмовский ID +* Отрядный? +* Экспа за неделю (в боях) +* Экспа за месяц (в боях) +* Командир? +* Статус в лиге (обычный/топ/админ) + + +#### Бот в отряде + +* Пин за 5 минут "Скоро битва, мазафака" +* Приказы на атаку (отдельно) +* Велкам-бот: + * Если человек не в отряде по версии бота – кикать его и слать месседж командиру +* Командир задает боту участие игрока в отряде + +#### Слушаем форварды + +* Поймал покемона – поздравить +* Отдельно рарник – поздравить +* По уровню тоже делить тексты +* Форвард левелапа – грацать + +#### Сорвенования + + +#### Апдейты + +* Профиль живет три дня, потом просим апдейтить. +* После 6 дней сообщение командиру или в общий чат, если человек не в отряде diff --git a/config.yml.example b/config.yml.example new file mode 100644 index 0000000..0d3e589 --- /dev/null +++ b/config.yml.example @@ -0,0 +1,8 @@ +telegram_connection: + api_token: "your-awesome-token" +database_connection: + host: "localhost" + port: "3306" + user: "i2_bot" + password: "i2_bot" + database: "i2_bot" diff --git a/i2_bot.go b/i2_bot.go new file mode 100644 index 0000000..9287a2c --- /dev/null +++ b/i2_bot.go @@ -0,0 +1,40 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package main + +import ( + // stdlib + "time" + // 3rd-party + "gopkg.in/telegram-bot-api.v4" + // local + "./lib/appcontext" + "./lib/router" +) + +var ( + c *appcontext.Context +) + +func main() { + c := appcontext.New() + c.Init() + router.New(c) + + u := tgbotapi.NewUpdate(0) + u.Timeout = 60 + + updates, _ := c.Bot.GetUpdatesChan(u) + + for update := range updates { + if update.Message == nil { + continue + } else if update.Message.Date < (int(time.Now().Unix()) - 1) { + // Ignore old messages + continue + } + + c.Router.RouteRequest(update) + } +} diff --git a/lib/appcontext/appcontext.go b/lib/appcontext/appcontext.go new file mode 100644 index 0000000..93d23c1 --- /dev/null +++ b/lib/appcontext/appcontext.go @@ -0,0 +1,34 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package appcontext + +import ( + // 3rd-party + "github.com/jmoiron/sqlx" + "gopkg.in/telegram-bot-api.v4" + // local + "../config" + "../connections" + // interfaces + "../router/routerinterface" +) + +type Context struct { + Cfg *config.Config + Bot *tgbotapi.BotAPI + Router routerinterface.RouterInterface + Db *sqlx.DB +} + +func (c *Context) Init() { + c.Cfg = config.New() + c.Cfg.Init() + c.Bot = connections.BotInit(c.Cfg) + c.Db = connections.DBInit(c.Cfg) +} + +func (c *Context) RegisterRouterInterface(ri routerinterface.RouterInterface) { + c.Router = ri + c.Router.Init() +} diff --git a/lib/appcontext/exported.go b/lib/appcontext/exported.go new file mode 100644 index 0000000..4d9375e --- /dev/null +++ b/lib/appcontext/exported.go @@ -0,0 +1,13 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package appcontext + +var ( + a *Context +) + +func New() *Context { + c := &Context{} + return c +} diff --git a/lib/config/config.go b/lib/config/config.go new file mode 100644 index 0000000..e45da6c --- /dev/null +++ b/lib/config/config.go @@ -0,0 +1,50 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package config + +import ( + // stdlib + "io/ioutil" + "log" + "path/filepath" + // 3rd-party + "gopkg.in/yaml.v2" +) + +const VERSION = "0.03" + +type DatabaseConnection struct { + Host string `yaml:"host"` + Port string `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` + Database string `yaml:"database"` +} + +type TelegramConnection struct { + APIToken string `yaml:"api_token"` +} + +type Config struct { + Telegram TelegramConnection `yaml:"telegram_connection"` + Database DatabaseConnection `yaml:"database_connection"` +} + +func (c *Config) Init() { + fname, _ := filepath.Abs("./config.yml") + yamlFile, yerr := ioutil.ReadFile(fname) + if yerr != nil { + log.Fatal("Can't read config file") + } + + yperr := yaml.Unmarshal(yamlFile, c) + if yperr != nil { + log.Fatal("Can't parse config file") + } +} + +func New() *Config { + c := &Config{} + return c +} diff --git a/lib/connections/connections.go b/lib/connections/connections.go new file mode 100644 index 0000000..715770d --- /dev/null +++ b/lib/connections/connections.go @@ -0,0 +1,38 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package connections + +import ( + // stdlib + "log" + // 3rd-party + "gopkg.in/telegram-bot-api.v4" + "github.com/jmoiron/sqlx" + _ "github.com/go-sql-driver/mysql" + // local + "../config" +) + +func BotInit(cfg *config.Config) *tgbotapi.BotAPI { + bot, err := tgbotapi.NewBotAPI(cfg.Telegram.APIToken) + if err != nil { + log.Panic(err) + } + + bot.Debug = true + + log.Printf("Bot version: " + config.VERSION) + log.Printf("Authorized on account %s", bot.Self.UserName) + + return bot +} + +func DBInit(cfg *config.Config) *sqlx.DB { + database, err := sqlx.Connect("mysql", cfg.Database.User + ":" + cfg.Database.Password + "@tcp(" + cfg.Database.Host + ":" + cfg.Database.Port + ")/" + cfg.Database.Database + "?parseTime=true&charset=utf8mb4,utf8") + if err != nil { + log.Fatal(err) + } + log.Printf("Database connection established!") + return database +} diff --git a/lib/router/exported.go b/lib/router/exported.go new file mode 100644 index 0000000..98a9851 --- /dev/null +++ b/lib/router/exported.go @@ -0,0 +1,26 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package router + +import ( + // stdlib + "log" + // local + "../appcontext" +) + +var ( + c *appcontext.Context + r *Router +) + +func New(ac *appcontext.Context) { + c = ac + rh := RouterHandler{} + c.RegisterRouterInterface(rh) +} + +func (r *Router) Init() { + log.Printf("Initialized request router...") +} diff --git a/lib/router/handler.go b/lib/router/handler.go new file mode 100644 index 0000000..f18c73d --- /dev/null +++ b/lib/router/handler.go @@ -0,0 +1,19 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package router + +import ( + // 3rd party + "gopkg.in/telegram-bot-api.v4" +) + +type RouterHandler struct {} + +func (rh RouterHandler) Init() { + r.Init() +} + +func (rh RouterHandler) RouteRequest(update tgbotapi.Update) string { + return r.RouteRequest(update) +} diff --git a/lib/router/router.go b/lib/router/router.go new file mode 100644 index 0000000..4375c51 --- /dev/null +++ b/lib/router/router.go @@ -0,0 +1,51 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package router + +import ( + // stdlib + "log" + "regexp" + // 3rd party + "gopkg.in/telegram-bot-api.v4" + // local + "../talkers" +) + +type Router struct {} + +// This function will route requests to appropriative modules +// It will return "ok" or "fail" +// If command doesn't exist, it's "fail" +func (r *Router) RouteRequest(update tgbotapi.Update) string { + text := update.Message.Text + + // Regular expressions + var durakMsg = regexp.MustCompile("(Д|д)(У|у)(Р|р)(А|а|Е|е|О|о)") + var huMsg = regexp.MustCompile("(Х|х)(У|у)(Й|й|Я|я|Ю|ю|Е|е)") + var blMsg = regexp.MustCompile("\\s(Б|б)(Л|л)(Я|я)(Т|т|Д|д)") + var ebMsg = regexp.MustCompile("(Е|е|Ё|ё)(Б|б)(\\s|А|а|Т|т|У|у|Е|е|Ё|ё|И|и)") + var piMsg = regexp.MustCompile("(П|п)(И|и)(З|з)(Д|д)") + var helpMsg = regexp.MustCompile("/help\\z") + + switch { + case helpMsg.MatchString(text): + talkers.HelpMessage(c.Bot, update) + case huMsg.MatchString(text): + talkers.MatMessage(c.Bot, update) + case blMsg.MatchString(text): + talkers.MatMessage(c.Bot, update) + case ebMsg.MatchString(text): + talkers.MatMessage(c.Bot, update) + case piMsg.MatchString(text): + talkers.MatMessage(c.Bot, update) + case durakMsg.MatchString(text): + talkers.DurakMessage(c.Bot, update) + default: + log.Printf("User posted unknown command.") + return "fail" + } + + return "ok" +} diff --git a/lib/router/routerinterface/routerinterface.go b/lib/router/routerinterface/routerinterface.go new file mode 100644 index 0000000..5683212 --- /dev/null +++ b/lib/router/routerinterface/routerinterface.go @@ -0,0 +1,14 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package routerinterface + +import ( + // 3rd party + "gopkg.in/telegram-bot-api.v4" +) + +type RouterInterface interface { + Init() + RouteRequest(update tgbotapi.Update) string +} diff --git a/lib/talkers/easter.go b/lib/talkers/easter.go new file mode 100644 index 0000000..eef0e82 --- /dev/null +++ b/lib/talkers/easter.go @@ -0,0 +1,48 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package talkers + +import ( + // stdlib + "log" + "math/rand" + "time" + // 3rd party + "gopkg.in/telegram-bot-api.v4" +) + +func DurakMessage(bot *tgbotapi.BotAPI, update tgbotapi.Update) { + log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) + + reactions := make([]string, 0) + reactions = append(reactions, "Сам такой!", + "А ты типа нет?", + "Фу, как некультурно!", + "Профессор, если вы такой умный, то почему вы такой бедный? /donate", + "Попка – не дурак, Попка – самый непадающий бот!") + + // Praise the Random Gods! + rand.Seed(time.Now().Unix()) + msg := tgbotapi.NewMessage(update.Message.Chat.ID, reactions[rand.Intn(len(reactions))]) + msg.ReplyToMessageID = update.Message.MessageID + + bot.Send(msg) +} + +func MatMessage(bot *tgbotapi.BotAPI, update tgbotapi.Update) { + log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) + + reactions := make([]string, 0) + reactions = append(reactions, "Фу, как некультурно!", + "Иди рот с мылом помой", + "Тшшшш!", + "Да я твою мамку в кино водил!") + + // Praise the Random Gods! + rand.Seed(time.Now().Unix()) + msg := tgbotapi.NewMessage(update.Message.Chat.ID, reactions[rand.Intn(len(reactions))]) + msg.ReplyToMessageID = update.Message.MessageID + + bot.Send(msg) +} diff --git a/lib/talkers/help.go b/lib/talkers/help.go new file mode 100644 index 0000000..2b006f8 --- /dev/null +++ b/lib/talkers/help.go @@ -0,0 +1,25 @@ +// i2_bot – Instinct PokememBro Bot +// Copyright (c) 2017 Vladimir "fat0troll" Hodakov + +package talkers + +import ( + // 3rd party + "gopkg.in/telegram-bot-api.v4" + // local + "../config" +) + +func HelpMessage(bot *tgbotapi.BotAPI, update tgbotapi.Update) { + help_message := "*Бот Инстинкта. Версия обезшпионенная и улучшенная.*\n\n" + help_message += "Текущая версия: *" + config.VERSION + "*\n\n" + help_message += "Список команд:\n\n" + help_message += "+ /help – выводит данное сообщение\n" + help_message += "\n\n" + help_message += "Связаться с автором: @fat0troll\n" + + msg := tgbotapi.NewMessage(update.Message.Chat.ID, help_message) + msg.ParseMode = "Markdown" + + bot.Send(msg) +}