Add slack message retrieval

This commit is contained in:
erroneousboat 2016-09-25 22:34:02 +02:00
parent c9961d6cd8
commit 555184a033
11 changed files with 326 additions and 80 deletions

View File

@ -15,7 +15,3 @@ Examples:
- https://github.com/nsf/godit - https://github.com/nsf/godit
- https://github.com/moncho/dry - https://github.com/moncho/dry
- https://github.com/mikepea/go-jira-ui - https://github.com/mikepea/go-jira-ui
- [ ] show cursor, see if you can alter Par
- [ ] input overflow
- [ ] chat

View File

@ -0,0 +1,56 @@
package components
import (
"github.com/erroneousboat/slack-term/src/service"
"github.com/gizak/termui"
)
type Channels struct {
List *termui.List
}
func CreateChannels(svc *service.SlackService, inputHeight int) *Channels {
channels := &Channels{
List: termui.NewList(),
}
channels.List.BorderLabel = "Channels"
channels.List.Overflow = "wrap"
channels.List.Height = termui.TermHeight() - inputHeight
channels.GetChannels(svc)
return channels
}
// Buffer implements interface termui.Bufferer
func (c *Channels) Buffer() termui.Buffer {
buf := c.List.Buffer()
return buf
}
// GetHeight implements interface termui.GridBufferer
func (c *Channels) GetHeight() int {
return c.List.Block.GetHeight()
}
// SetWidth implements interface termui.GridBufferer
func (c *Channels) SetWidth(w int) {
c.List.SetWidth(w)
}
// SetX implements interface termui.GridBufferer
func (c *Channels) SetX(x int) {
c.List.SetX(x)
}
// SetY implements interface termui.GridBufferer
func (c *Channels) SetY(y int) {
c.List.SetY(y)
}
func (c *Channels) GetChannels(svc *service.SlackService) {
for _, slackChan := range svc.GetChannels() {
c.List.Items = append(c.List.Items, slackChan.Name)
}
}

View File

@ -3,21 +3,26 @@ package components
import ( import (
"strings" "strings"
"github.com/erroneousboat/slack-term/src/service"
"github.com/gizak/termui" "github.com/gizak/termui"
) )
type Chat struct { type Chat struct {
List *termui.List List *termui.List
SelectedChannel string
} }
func CreateChat(inputHeight int) *Chat { func CreateChat(svc *service.SlackService, inputHeight int) *Chat {
chat := &Chat{ chat := &Chat{
List: termui.NewList(), List: termui.NewList(),
} }
// TODO: should be SetSelectedChannel
chat.SelectedChannel = svc.GetChannels()[0].ID
chat.List.Height = termui.TermHeight() - inputHeight chat.List.Height = termui.TermHeight() - inputHeight
chat.List.Overflow = "wrap" chat.List.Overflow = "wrap"
chat.LoadMessages() chat.GetMessages(svc)
return chat return chat
} }
@ -125,18 +130,15 @@ func (c *Chat) SetY(y int) {
c.List.SetY(y) c.List.SetY(y)
} }
func (c *Chat) LoadMessages() { func (c *Chat) GetMessages(svc *service.SlackService) {
messages := []string{ messages := svc.GetMessages(c.SelectedChannel)
"[jp] hello world",
"[erroneousboat] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar",
}
for _, message := range messages { for _, message := range messages {
c.AddMessages(message) c.AddMessage(message)
} }
} }
func (c *Chat) AddMessages(message string) { func (c *Chat) AddMessage(message string) {
c.List.Items = append(c.List.Items, message) c.List.Items = append(c.List.Items, message)
} }

View File

@ -1,45 +0,0 @@
package components
import "github.com/gizak/termui"
func CreateInputComponent() *termui.Par {
compInput := termui.NewPar("")
compInput.Height = 3
return compInput
}
func CreateChannelsComponent(inputHeight int) *termui.List {
channels := []string{
"general",
"random",
}
compChannels := termui.NewList()
compChannels.Items = channels
compChannels.BorderLabel = "Channels"
compChannels.Height = termui.TermHeight() - inputHeight
compChannels.Overflow = "wrap"
return compChannels
}
func CreateChatComponent(inputHeight int) *termui.List {
messages := []string{
"[jp] hello world",
"[erroneousboat] foo bar",
}
compChat := termui.NewList()
compChat.Items = messages
compChat.BorderLabel = "Channel01"
compChat.Height = termui.TermHeight() - inputHeight
compChat.Overflow = "wrap"
return compChat
}
func CreateModeComponent() *termui.Par {
compMode := termui.NewPar("NORMAL")
compMode.Height = 3
return compMode
}

43
src/components/mode.go Normal file
View File

@ -0,0 +1,43 @@
package components
import "github.com/gizak/termui"
type Mode struct {
Par *termui.Par
}
func CreateMode() *Mode {
mode := &Mode{
Par: termui.NewPar("NORMAL"),
}
mode.Par.Height = 3
return mode
}
// Buffer implements interface termui.Bufferer
func (m *Mode) Buffer() termui.Buffer {
buf := m.Par.Buffer()
return buf
}
// GetHeight implements interface termui.GridBufferer
func (m *Mode) GetHeight() int {
return m.Par.Block.GetHeight()
}
// SetWidth implements interface termui.GridBufferer
func (m *Mode) SetWidth(w int) {
m.Par.SetWidth(w)
}
// SetX implements interface termui.GridBufferer
func (m *Mode) SetX(x int) {
m.Par.SetX(x)
}
// SetY implements interface termui.GridBufferer
func (m *Mode) SetY(y int) {
m.Par.SetY(y)
}

25
src/config/config.go Normal file
View File

@ -0,0 +1,25 @@
package config
import (
"encoding/json"
"os"
)
type Config struct {
SlackToken string `json:"slack_token"`
}
func NewConfig(filepath string) (*Config, error) {
var cfg Config
file, err := os.Open(filepath)
if err != nil {
return &cfg, err
}
if err := json.NewDecoder(file).Decode(&cfg); err != nil {
return &cfg, err
}
return &cfg, nil
}

View File

@ -1,8 +1,13 @@
package context package context
import ( import (
"github.com/erroneousboat/slack-term/src/views" "log"
"github.com/gizak/termui" "github.com/gizak/termui"
"github.com/erroneousboat/slack-term/src/config"
"github.com/erroneousboat/slack-term/src/service"
"github.com/erroneousboat/slack-term/src/views"
) )
const ( const (
@ -11,17 +16,30 @@ const (
) )
type AppContext struct { type AppContext struct {
Body *termui.Grid Service *service.SlackService
View *views.View Body *termui.Grid
Mode string View *views.View
Config *config.Config
Mode string
} }
// TODO: arg Config func CreateAppContext(flgConfig string) *AppContext {
func CreateAppContext() *AppContext { // Load config
view := views.CreateChatView() config, err := config.NewConfig(flgConfig)
if err != nil {
log.Fatalf("ERROR: not able to load config file: %s", flgConfig)
}
// Create Service
svc := service.NewSlackService(config.SlackToken)
// Create ChatView
view := views.CreateChatView(svc)
return &AppContext{ return &AppContext{
View: view, Service: svc,
Mode: CommandMode, View: view,
Config: config,
Mode: CommandMode,
} }
} }

View File

@ -1,7 +1,10 @@
package handlers package handlers
import ( import (
"fmt"
"github.com/gizak/termui" "github.com/gizak/termui"
"github.com/nlopes/slack"
"github.com/erroneousboat/slack-term/src/context" "github.com/erroneousboat/slack-term/src/context"
"github.com/erroneousboat/slack-term/src/views" "github.com/erroneousboat/slack-term/src/views"
@ -10,6 +13,28 @@ import (
func RegisterEventHandlers(ctx *context.AppContext) { func RegisterEventHandlers(ctx *context.AppContext) {
termui.Handle("/sys/kbd/", anyKeyHandler(ctx)) termui.Handle("/sys/kbd/", anyKeyHandler(ctx))
termui.Handle("/sys/wnd/resize", resizeHandler(ctx)) termui.Handle("/sys/wnd/resize", resizeHandler(ctx))
termui.Handle("/timer/1s", timeHandler(ctx))
// TODO: check channel of message should be added to correct channel
go func() {
for {
select {
case msg := <-ctx.Service.RTM.IncomingEvents:
switch ev := msg.Data.(type) {
case *slack.MessageEvent:
var name string
user, err := ctx.Service.Client.GetUserInfo(ev.User)
if err == nil {
name = user.Name
} else {
name = "unknown"
}
msg := fmt.Sprintf("[%s] %s", name, ev.Text)
ctx.View.Chat.AddMessage(msg)
}
}
}
}()
} }
func anyKeyHandler(ctx *context.AppContext) func(termui.Event) { func anyKeyHandler(ctx *context.AppContext) func(termui.Event) {
@ -60,6 +85,11 @@ func resizeHandler(ctx *context.AppContext) func(termui.Event) {
} }
} }
func timeHandler(ctx *context.AppContext) func(termui.Event) {
return func(e termui.Event) {
}
}
// FIXME: resize only seems to work for width and resizing it too small // FIXME: resize only seems to work for width and resizing it too small
// will cause termui to panic // will cause termui to panic
func actionResize(ctx *context.AppContext) { func actionResize(ctx *context.AppContext) {
@ -103,12 +133,23 @@ func actionQuit() {
func actionInsertMode(ctx *context.AppContext) { func actionInsertMode(ctx *context.AppContext) {
ctx.Mode = context.InsertMode ctx.Mode = context.InsertMode
ctx.View.Mode.Text = "INSERT" ctx.View.Mode.Par.Text = "INSERT"
termui.Render(ctx.View.Mode) termui.Render(ctx.View.Mode)
} }
func actionCommandMode(ctx *context.AppContext) { func actionCommandMode(ctx *context.AppContext) {
ctx.Mode = context.CommandMode ctx.Mode = context.CommandMode
ctx.View.Mode.Text = "NORMAL" ctx.View.Mode.Par.Text = "NORMAL"
termui.Render(ctx.View.Mode) termui.Render(ctx.View.Mode)
} }
// TODO: get message for channel
func actionGetMessages(ctx *context.AppContext) {
ctx.View.Chat.GetMessages(ctx.Service)
termui.Render(ctx.View.Chat)
}
func actionGetChannels(ctx *context.AppContext) {
ctx.View.Channels.GetChannels(ctx.Service)
termui.Render(ctx.View.Channels)
}

View File

@ -1,6 +1,11 @@
package main package main
import ( import (
"flag"
"log"
"os/user"
"path"
"github.com/erroneousboat/slack-term/src/context" "github.com/erroneousboat/slack-term/src/context"
"github.com/erroneousboat/slack-term/src/handlers" "github.com/erroneousboat/slack-term/src/handlers"
@ -8,16 +13,31 @@ import (
) )
func main() { func main() {
// Start terminal user interface
err := termui.Init() err := termui.Init()
if err != nil { if err != nil {
panic(err) panic(err)
} }
defer termui.Close() defer termui.Close()
// create context // Get home dir for config file default
ctx := context.CreateAppContext() usr, err := user.Current()
if err != nil {
log.Fatal(err)
}
// setup view // Parse flags
flgConfig := flag.String(
"config",
path.Join(usr.HomeDir, "slack-term.json"),
"location of config file",
)
flag.Parse()
// Create context
ctx := context.CreateAppContext(*flgConfig)
// Setup body
termui.Body.AddRows( termui.Body.AddRows(
termui.NewRow( termui.NewRow(
termui.NewCol(1, 0, ctx.View.Channels), termui.NewCol(1, 0, ctx.View.Channels),
@ -31,9 +51,10 @@ func main() {
termui.Body.Align() termui.Body.Align()
termui.Render(termui.Body) termui.Render(termui.Body)
// Set body in context
ctx.Body = termui.Body ctx.Body = termui.Body
// register handlers // Register handlers
handlers.RegisterEventHandlers(ctx) handlers.RegisterEventHandlers(ctx)
termui.Loop() termui.Loop()

88
src/service/slack.go Normal file
View File

@ -0,0 +1,88 @@
package service
import (
"fmt"
"log"
"github.com/nlopes/slack"
)
type SlackService struct {
Client *slack.Client
RTM *slack.RTM
Channels []slack.Channel
}
type Channel struct {
ID string
Name string
}
func NewSlackService(token string) *SlackService {
svc := new(SlackService)
svc.Client = slack.New(token)
svc.RTM = svc.Client.NewRTM()
go svc.RTM.ManageConnection()
return svc
}
func (s *SlackService) Connect() {
}
func (s *SlackService) GetChannels() []Channel {
var chans []Channel
slackChans, err := s.Client.GetChannels(true)
if err != nil {
chans = append(chans, Channel{})
}
s.Channels = slackChans
for _, slackChan := range slackChans {
chans = append(chans, Channel{slackChan.ID, slackChan.Name})
}
return chans
}
func (s *SlackService) SendMessage(message string) {}
func (s *SlackService) GetMessages(channel string) []string {
// https://api.slack.com/methods/channels.history
historyParams := slack.HistoryParameters{
// Latest: "now",
// Oldest: 0,
Count: 50,
Inclusive: false,
Unreads: false,
}
// https://godoc.org/github.com/nlopes/slack#History
history, err := s.Client.GetChannelHistory(channel, historyParams)
if err != nil {
log.Fatal(err)
return []string{""}
}
// TODO: this takes a long time, maybe use some dynamic programming
var messages []string
for _, message := range history.Messages {
var name string
user, err := s.Client.GetUserInfo(message.User)
if err == nil {
name = user.Name
} else {
name = "unknown"
}
msg := fmt.Sprintf("[%s] %s", name, message.Text)
messages = append(messages, msg)
}
return messages
}

View File

@ -2,6 +2,7 @@ package views
import ( import (
"github.com/erroneousboat/slack-term/src/components" "github.com/erroneousboat/slack-term/src/components"
"github.com/erroneousboat/slack-term/src/service"
"github.com/gizak/termui" "github.com/gizak/termui"
) )
@ -9,15 +10,15 @@ import (
type View struct { type View struct {
Input *components.Input Input *components.Input
Chat *components.Chat Chat *components.Chat
Channels *termui.List Channels *components.Channels
Mode *termui.Par Mode *components.Mode
} }
func CreateChatView() *View { func CreateChatView(svc *service.SlackService) *View {
input := components.CreateInput() input := components.CreateInput()
channels := components.CreateChannelsComponent(input.Par.Height) channels := components.CreateChannels(svc, input.Par.Height)
chat := components.CreateChat(input.Par.Height) chat := components.CreateChat(svc, input.Par.Height)
mode := components.CreateModeComponent() mode := components.CreateMode()
view := &View{ view := &View{
Input: input, Input: input,