[WIP] Implement cmdParser(), fix some linter warnings
This commit is contained in:
10
config.go
10
config.go
@@ -20,7 +20,7 @@ func parseConfig(path string) (myConfig, error) {
|
|||||||
|
|
||||||
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата.
|
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата.
|
||||||
if fileInfo.Size() > 65535 {
|
if fileInfo.Size() > 65535 {
|
||||||
err := fmt.Errorf("Config file %s is too long for config, skipping", path)
|
err := fmt.Errorf("config file %s is too long for config, skipping", path)
|
||||||
|
|
||||||
return myConfig{}, err
|
return myConfig{}, err
|
||||||
}
|
}
|
||||||
@@ -70,13 +70,13 @@ func parseConfig(path string) (myConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Redis.Channel == "" {
|
if sampleConfig.Redis.Channel == "" {
|
||||||
err := fmt.Errorf("Channel field in config file %s must be set", path)
|
err := fmt.Errorf("channel field in config file %s must be set", path)
|
||||||
|
|
||||||
return myConfig{}, err
|
return myConfig{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Redis.MyChannel == "" {
|
if sampleConfig.Redis.MyChannel == "" {
|
||||||
err := fmt.Errorf("My_channel field in config file %s must be set", path)
|
err := fmt.Errorf("my_channel field in config file %s must be set", path)
|
||||||
|
|
||||||
return myConfig{}, err
|
return myConfig{}, err
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ func parseConfig(path string) (myConfig, error) {
|
|||||||
// sampleConfig.Log = "" if not set
|
// sampleConfig.Log = "" if not set
|
||||||
|
|
||||||
if sampleConfig.Csign == "" {
|
if sampleConfig.Csign == "" {
|
||||||
err := fmt.Errorf("Csign field in config file %s must be set", path)
|
err := fmt.Errorf("csign field in config file %s must be set", path)
|
||||||
|
|
||||||
return myConfig{}, err
|
return myConfig{}, err
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ func parseConfig(path string) (myConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.DataDir == "" {
|
if sampleConfig.DataDir == "" {
|
||||||
return myConfig{}, fmt.Errorf("Data_dir field in config file %s must be set", path)
|
return myConfig{}, fmt.Errorf("data_dir field in config file %s must be set", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sampleConfig, err
|
return sampleConfig, err
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ var subscriber *redis.PubSub
|
|||||||
// Main context.
|
// Main context.
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
|
|
||||||
// Ставится в true, если мы получили сигнал на выключение
|
// Ставится в true, если мы получили сигнал на выключение.
|
||||||
var shutdown = false
|
var shutdown = false
|
||||||
|
|
||||||
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
||||||
|
|||||||
167
msg-parsers.go
167
msg-parsers.go
@@ -237,15 +237,20 @@ func telegramMsgParser(msg *echotron.Update) {
|
|||||||
|
|
||||||
// TODO: если человек ответил на авто-приветствие, это надо обнаруживать и пропускать.
|
// TODO: если человек ответил на авто-приветствие, это надо обнаруживать и пропускать.
|
||||||
|
|
||||||
// Если фраза является командой - засылаем её в парсер команд.
|
// Pасылаем сообщение в парсер команд.
|
||||||
if msg.Message.Text[0:len(config.Csign)] == config.Csign {
|
res, err := cmdParser(me, msg)
|
||||||
if err := cmdParser(msg.Message.Text); err != nil {
|
|
||||||
|
if err != nil {
|
||||||
log.Errorf(
|
log.Errorf(
|
||||||
"Unable to parse message from telegram api as a command. Message was: %s, error: %s",
|
"Unable to parse message from telegram api as a command. Message was: %s, error: %s",
|
||||||
msg.Message.Text,
|
msg.Message.Text,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если сообщение - команда, то на этом наши приключения закончились.
|
||||||
|
if res {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Здесь же, если фраза обращена к боту (ник или имя), выставляем флажок, что надо ответить
|
// Здесь же, если фраза обращена к боту (ник или имя), выставляем флажок, что надо ответить
|
||||||
@@ -354,10 +359,130 @@ func telegramMsgParser(msg *echotron.Update) {
|
|||||||
} //nolint:wsl
|
} //nolint:wsl
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdParser(cmd string) error {
|
// cmdParser разбирает сообщение как команду и засылает его в роутер, попутно обкладывая корректными значениями
|
||||||
var err error
|
// параметров. Возвращает true если это была команда.
|
||||||
|
func cmdParser(me echotron.APIResponseUser, cmd *echotron.Update) (bool, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
smsg sMsg
|
||||||
|
bingo bool
|
||||||
|
)
|
||||||
|
|
||||||
return err
|
smsg.From = "telegram"
|
||||||
|
smsg.Plugin = "telegram"
|
||||||
|
smsg.Chatid = fmt.Sprintf("%d", cmd.Message.Chat.ID)
|
||||||
|
smsg.Userid = fmt.Sprintf("%d", cmd.Message.From.ID)
|
||||||
|
smsg.Message = cmd.Message.Text
|
||||||
|
smsg.Misc.Answer = 1
|
||||||
|
|
||||||
|
// Тоже самое можно проставить выковырнув значение из cmd.Message.Chat.Type, где group/supergroup/channel - это
|
||||||
|
// public, а остальное - private, но по id проще/быстрее, хоть это и хак.
|
||||||
|
if cmd.Message.Chat.ID >= 0 {
|
||||||
|
smsg.Mode = "private"
|
||||||
|
} else {
|
||||||
|
smsg.Mode = "public"
|
||||||
|
}
|
||||||
|
|
||||||
|
smsg.Misc.Botnick = ConstructPartialUserUsername(me.Result)
|
||||||
|
|
||||||
|
// Предполагаем, что длина cmd.Message.Text всегда больше или равна длине config.Csign.
|
||||||
|
if cmd.Message.Text[0:len(config.Csign)] == config.Csign {
|
||||||
|
// Повторно проверяем, что текст является простой командой.
|
||||||
|
|
||||||
|
command := cmd.Message.Text[len(config.Csign):]
|
||||||
|
|
||||||
|
// TODO: поддержать команды ver version версия, help, admin и admin *.
|
||||||
|
|
||||||
|
// Команды в одно слово.
|
||||||
|
cmds := []string{
|
||||||
|
"ping", "пинг", "пинх", "pong", "понг", "понх", "coin", "монетка", "roll", "dice", "кости", "ver",
|
||||||
|
"version", "версия", "хэлп", "halp", "kde", "кде", "lat", "лат", "friday", "пятница", "proverb",
|
||||||
|
"пословица", "пословиться", "fortune", "фортунка", "f", "ф", "anek", "анек", "анекдот", "buni", "cat",
|
||||||
|
"кис", "drink", "праздник", "fox", "лис", "frog", "лягушка", "horse", "лошадь", "лошадка", "monkeyuser",
|
||||||
|
"owl", "сова", "сыч", "rabbit", "bunny", "кролик", "snail", "улитка", "tits", "boobs", "tities", "boobies",
|
||||||
|
"сиси", "сисечки", "butt", "booty", "ass", "попа", "попка", "xkcd", "dig", "копать", "fish", "fishing",
|
||||||
|
"рыба", "рыбка", "рыбалка", "karma", "карма", "fuck",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cmds {
|
||||||
|
if command == c {
|
||||||
|
bingo = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Не нашлось команды в одно слово. Поищем команды с одним аргументом.
|
||||||
|
if !bingo {
|
||||||
|
cmds = []string{
|
||||||
|
"w", "п", "weather", "погода", "погодка", "погадка", "karma", "карма",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cmds {
|
||||||
|
cRe := fmt.Sprintf("^%s ", c)
|
||||||
|
r := regexp.MustCompile(cRe)
|
||||||
|
|
||||||
|
if r.MatchString(command) {
|
||||||
|
bingo = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Что-то странное пришло. Залоггируем и ничего делать не будем. Просто свалим.
|
||||||
|
if !bingo {
|
||||||
|
log.Infof(
|
||||||
|
"Strange command from %s %s: %s",
|
||||||
|
ConstructFullChatName(&cmd.Message.Chat),
|
||||||
|
ConstructFullUserName(cmd.Message.From),
|
||||||
|
cmd.Message.Text,
|
||||||
|
)
|
||||||
|
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для некоторых команд надо подсвечивать имя пользователя в ответе.
|
||||||
|
cmds = []string{
|
||||||
|
"dig", "копать", "fish", "fishing", "рыба", "рыбка", "рыбалка",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cmds {
|
||||||
|
// В vscode возникает ошибка при использовании if-а.
|
||||||
|
switch {
|
||||||
|
case command == c:
|
||||||
|
smsg.Misc.Username = ConstructTelegramHighlightName(cmd.Message.From)
|
||||||
|
smsg.Misc.Msgformat = 1
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если у нас супергруппа с тредами, надо проставить thread id.
|
||||||
|
if cmd.Message.Chat.Type == "supergroup" && cmd.Message.IsTopicMessage && cmd.Message.ThreadID != 0 {
|
||||||
|
smsg.ThreadID = fmt.Sprintf("%d", cmd.Message.ThreadID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляем сообщение в редиску.
|
||||||
|
data, err := json.Marshal(smsg)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||||
|
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Заталкиваем наш json в редиску.
|
||||||
|
if err := redisClient.Publish(ctx, config.Redis.Channel, data).Err(); err != nil {
|
||||||
|
log.Warnf("Unable to send data to redis channel %s: %s", config.Redis.Channel, err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Censor парсит сообщения в поисках непотребных данных и если он их находит, то сообщение удаляется.
|
// Censor парсит сообщения в поисках непотребных данных и если он их находит, то сообщение удаляется.
|
||||||
@@ -386,7 +511,7 @@ func Censor(msg *echotron.Update) bool {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstructFullUserName выковыривает из сообщения полный username, в формате @username FirstName LastName (id)
|
// ConstructFullUserName выковыривает из сообщения полный username, в формате @username FirstName LastName (id).
|
||||||
func ConstructFullUserName(u *echotron.User) string {
|
func ConstructFullUserName(u *echotron.User) string {
|
||||||
user := fmt.Sprintf("(%d)", u.ID)
|
user := fmt.Sprintf("(%d)", u.ID)
|
||||||
|
|
||||||
@@ -405,7 +530,7 @@ func ConstructFullUserName(u *echotron.User) string {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstructFullChatName выковыривает из сообщения полный username чата, в формате @username FirstName LastName (id)
|
// ConstructFullChatName выковыривает из сообщения полный username чата, в формате @username FirstName LastName (id).
|
||||||
func ConstructFullChatName(c *echotron.Chat) string {
|
func ConstructFullChatName(c *echotron.Chat) string {
|
||||||
chat := fmt.Sprintf("(%d)", c.ID)
|
chat := fmt.Sprintf("(%d)", c.ID)
|
||||||
|
|
||||||
@@ -487,4 +612,30 @@ func ConstructUserFirstLastName(u *echotron.User) string {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConstructTelegramHighlightName генерирует имя пользователя, которое триггерит на стороне клиент ивент меншена
|
||||||
|
// определённого пользователя.
|
||||||
|
func ConstructTelegramHighlightName(u *echotron.User) string {
|
||||||
|
var (
|
||||||
|
username string
|
||||||
|
link string
|
||||||
|
)
|
||||||
|
|
||||||
|
link = fmt.Sprintf("tg://user?id=%d", u.ID)
|
||||||
|
|
||||||
|
// Тут мы предполагаем, что как минимум либо firstname либо lastname либо username всегда есть. По сути так оно и
|
||||||
|
// должно быть.
|
||||||
|
switch {
|
||||||
|
case u.FirstName != "" && u.LastName != "":
|
||||||
|
username = fmt.Sprintf("%s %s", u.FirstName, u.LastName)
|
||||||
|
case u.FirstName != "":
|
||||||
|
username = u.FirstName
|
||||||
|
case u.LastName != "":
|
||||||
|
username = u.LastName
|
||||||
|
default:
|
||||||
|
username = u.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%s](%s)", username, link)
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import (
|
|||||||
|
|
||||||
// StoreKV сохраняет в указанной бд ключ и значение.
|
// StoreKV сохраняет в указанной бд ключ и значение.
|
||||||
func StoreKV(db *pebble.DB, key string, value string) error {
|
func StoreKV(db *pebble.DB, key string, value string) error {
|
||||||
var kArray = []byte(key)
|
var (
|
||||||
var vArray = []byte(value)
|
kArray = []byte(key)
|
||||||
|
vArray = []byte(value)
|
||||||
|
)
|
||||||
|
|
||||||
err := db.Set(kArray, vArray, pebble.Sync)
|
err := db.Set(kArray, vArray, pebble.Sync)
|
||||||
|
|
||||||
@@ -23,10 +25,12 @@ func StoreKV(db *pebble.DB, key string, value string) error {
|
|||||||
|
|
||||||
// FetchV достаёт значение по ключу.
|
// FetchV достаёт значение по ключу.
|
||||||
func FetchV(db *pebble.DB, key string) (string, error) {
|
func FetchV(db *pebble.DB, key string) (string, error) {
|
||||||
var kArray = []byte(key)
|
var (
|
||||||
|
kArray = []byte(key)
|
||||||
|
|
||||||
var vArray []byte
|
vArray []byte
|
||||||
var valueString = ""
|
valueString = ""
|
||||||
|
)
|
||||||
|
|
||||||
vArray, closer, err := db.Get(kArray)
|
vArray, closer, err := db.Get(kArray)
|
||||||
|
|
||||||
@@ -58,6 +62,7 @@ func GetSetting(chatID string, setting string) string {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,9 +89,11 @@ func GetSetting(chatID string, setting string) string {
|
|||||||
|
|
||||||
// saveSetting сохраняет настройку в БД с настройками.
|
// saveSetting сохраняет настройку в БД с настройками.
|
||||||
func GaveSetting(chatID string, setting string, value string) error {
|
func GaveSetting(chatID string, setting string, value string) error {
|
||||||
var chatHash = sha256.Sum256([]byte(chatID))
|
var (
|
||||||
var database = fmt.Sprintf("settings_db/%x", chatHash)
|
chatHash = sha256.Sum256([]byte(chatID))
|
||||||
var err error
|
database = fmt.Sprintf("settings_db/%x", chatHash)
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
// Если БД не открыта, откроем её.
|
// Если БД не открыта, откроем её.
|
||||||
if _, ok := settingsDB[database]; !ok {
|
if _, ok := settingsDB[database]; !ok {
|
||||||
@@ -100,6 +107,7 @@ func GaveSetting(chatID string, setting string, value string) error {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
types.go
7
types.go
@@ -25,6 +25,7 @@ type rMsg struct {
|
|||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
Chatid string `json:"chatid,omitempty"`
|
Chatid string `json:"chatid,omitempty"`
|
||||||
Userid string `json:"userid,omitempty"`
|
Userid string `json:"userid,omitempty"`
|
||||||
|
ThreadID string `json:"threadid,omitempty"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Plugin string `json:"plugin,omitempty"`
|
Plugin string `json:"plugin,omitempty"`
|
||||||
Mode string `json:"mode,omitempty"`
|
Mode string `json:"mode,omitempty"`
|
||||||
@@ -34,7 +35,12 @@ type rMsg struct {
|
|||||||
Csign string `json:"csign,omitempty"`
|
Csign string `json:"csign,omitempty"`
|
||||||
Fwdcnt int64 `json:"fwd_cnt,omitempty"`
|
Fwdcnt int64 `json:"fwd_cnt,omitempty"`
|
||||||
GoodMorning int64 `json:"good_morning,omitempty"`
|
GoodMorning int64 `json:"good_morning,omitempty"`
|
||||||
|
// В этом поле мы пишем 1, если бэкэнд должен в ответе форматировать сообщение в MarkdownV2, что какбэ
|
||||||
|
// не оч хорошо, так как мы выносим особенности реализации бэкэнда сюда.
|
||||||
Msgformat int64 `json:"msg_format,omitempty"`
|
Msgformat int64 `json:"msg_format,omitempty"`
|
||||||
|
// Судя по перловой реализации, сюда всегда попадает [$fullname](tg://user?id=$userid).
|
||||||
|
// Согласно докуметации, и перловой реализации бота, мы проставляем это поле тогда, когда мы точно знаем, что в
|
||||||
|
// бэкэнде оно используется.
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
} `json:"Misc"`
|
} `json:"Misc"`
|
||||||
}
|
}
|
||||||
@@ -44,6 +50,7 @@ type sMsg struct {
|
|||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
Chatid string `json:"chatid"`
|
Chatid string `json:"chatid"`
|
||||||
Userid string `json:"userid"`
|
Userid string `json:"userid"`
|
||||||
|
ThreadID string `json:"threadid,omitempty"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Plugin string `json:"plugin"`
|
Plugin string `json:"plugin"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
|
|||||||
Reference in New Issue
Block a user