diff --git a/internal/app/app.go b/internal/app/app.go index 0e38536..fcc488e 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -118,6 +118,7 @@ func (a *App) initHandlers() { handler.Message: handler.NewMessageHandler(a), handler.ChatMemberUpdated: handler.NewChatMemberUpdatedHandler(a), handler.CallbackQuery: handler.NewCallbackQueryHandler(a), + handler.InlineQuery: handler.NewInlineQueryHandler(a), } } @@ -131,6 +132,9 @@ func (a *App) handler(update telego.Update) handler.Handler { if update.CallbackQuery != nil { return a.Handlers[handler.CallbackQuery] } + if update.InlineQuery != nil { + return a.Handlers[handler.InlineQuery] + } return a.Handlers[handler.Noop] } diff --git a/internal/handler/callback_query_handler.go b/internal/handler/callback_query_handler.go index e575a46..f44956b 100644 --- a/internal/handler/callback_query_handler.go +++ b/internal/handler/callback_query_handler.go @@ -268,7 +268,7 @@ func (h *CallbackQueryHandler) getPollAfterEndKeyboard(chat *model.Chat, poll st if err != nil { return nil } - if rm != nil && rm.ID > 0 && poll.CanRedmine && poll.TaskID > 0 { + if rm != nil && rm.ID > 0 && poll.CanRedmine && poll.TaskID > 0 && len(poll.Votes) > 1 { return util.SendRedmineKeyboard(chat.TelegramID, poll.Result.RoundHalf, loc) } return nil diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 7297858..8e2ebb4 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -11,6 +11,7 @@ const ( Message ChatMemberUpdated CallbackQuery + InlineQuery ) type Handler interface { diff --git a/internal/handler/inline_query_handler.go b/internal/handler/inline_query_handler.go new file mode 100644 index 0000000..436521b --- /dev/null +++ b/internal/handler/inline_query_handler.go @@ -0,0 +1,57 @@ +package handler + +import ( + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "github.com/mymmrac/telego" + tu "github.com/mymmrac/telego/telegoutil" +) + +// kaomojis contains a list of some hand-picked kaomojis. +// +// Initially I wanted to make inline search feature for Redmine integration, but I realized that I don't have any data +// about target chat in the inline webhook. Ergo, I can't really say which Redmine integration to pick in case user +// is registered and added more than one chat. Also, other chat members won't be able to use the feature since they +// are not registered in the chat and Telegram doesn't allow us to get a list of chat members. +// +// Well, I tried. Maybe I'll do something with these limitations. Someday. Or not. Who knows? Not me! +var kaomojis = []string{ + "(●´ω`●)", + "( ͡° ͜ʖ ͡°)", + "¯\\_(ツ)_/¯", + "(╯°□°)╯︵ ┻━┻", + "┻━┻ ︵╰(°□°╰)", + "(☞゚ヮ゚)☞ ┻━┻", + "(ノಠ益ಠ)ノ", + "┬─┬ノ( º _ ºノ)", + "ლ(ಠ_ಠლ)", + "( つ ◕_◕ )つ", + "◔_◔", + "●_●", +} + +type InlineQueryHandler struct { + iface.Base +} + +func NewInlineQueryHandler(app iface.App) Handler { + return &InlineQueryHandler{iface.NewBase(app, 0, 0)} +} + +func (h *InlineQueryHandler) Handle(wh telego.Update) error { + return h.App.TG().AnswerInlineQuery(h.kaomojiResults(wh.InlineQuery.ID)) +} + +func (h *InlineQueryHandler) kaomojiResults(id string) *telego.AnswerInlineQueryParams { + results := make([]telego.InlineQueryResult, len(kaomojis)) + for i, kaomoji := range kaomojis { + results[i] = &telego.InlineQueryResultArticle{ + Type: "article", + ID: kaomoji, + Title: kaomoji, + InputMessageContent: &telego.InputTextMessageContent{ + MessageText: kaomoji, + }, + } + } + return tu.InlineQuery(id, results...) +} diff --git a/internal/integration/redmine/api/client.go b/internal/integration/redmine/api/client.go index e40bc97..78efd66 100644 --- a/internal/integration/redmine/api/client.go +++ b/internal/integration/redmine/api/client.go @@ -7,6 +7,7 @@ import ( "go.uber.org/zap" "io" "net/http" + "net/url" "strconv" "strings" "time" @@ -40,6 +41,23 @@ func (c *Client) Issue(id int) (*Issue, error) { return resp.Issue, nil } +func (c *Client) SearchIssues(query string, offset, limit int) (*SearchIssuesResponse, error) { + req := url.Values{ + "q": []string{query}, + "offset": []string{strconv.Itoa(offset)}, + "limit": []string{strconv.Itoa(limit)}, + } + var resp SearchIssuesResponse + _, err := c.sendJSONRequest(http.MethodGet, "/search.json?"+req.Encode(), nil, &resp) + if err != nil { + return nil, err + } + if resp.IsError() { + return nil, resp + } + return &resp, nil +} + func (c *Client) UpdateIssue(id int, issue *Issue) error { st, err := c.sendJSONRequest(http.MethodPut, "/issues/"+strconv.Itoa(id), IssueResponse{Issue: issue}, nil) if err != nil { diff --git a/internal/integration/redmine/api/dto.go b/internal/integration/redmine/api/dto.go index 345c9b3..b7e4e21 100644 --- a/internal/integration/redmine/api/dto.go +++ b/internal/integration/redmine/api/dto.go @@ -82,3 +82,20 @@ func (h *Hours) UnmarshalJSON(b []byte) error { *h = Hours(strconv.FormatFloat(f, 'f', 2, 64)) return nil } + +type SearchIssue struct { + ID int `json:"id"` + Title string `json:"title,omitempty"` + Type string `json:"type,omitempty"` + URL string `json:"url,omitempty"` + Description string `json:"description,omitempty"` + DateTime *time.Time `json:"datetime,omitempty"` +} + +type SearchIssuesResponse struct { + ErrorResponse + Results []SearchIssue `json:"results"` + TotalCount int `json:"total_count"` + Offset int `json:"offset"` + Limit int `json:"limit"` +}