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/moncho/dry
- 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 (
"strings"
"github.com/erroneousboat/slack-term/src/service"
"github.com/gizak/termui"
)
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{
List: termui.NewList(),
}
// TODO: should be SetSelectedChannel
chat.SelectedChannel = svc.GetChannels()[0].ID
chat.List.Height = termui.TermHeight() - inputHeight
chat.List.Overflow = "wrap"
chat.LoadMessages()
chat.GetMessages(svc)
return chat
}
@ -125,18 +130,15 @@ func (c *Chat) SetY(y int) {
c.List.SetY(y)
}
func (c *Chat) LoadMessages() {
messages := []string{
"[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",
}
func (c *Chat) GetMessages(svc *service.SlackService) {
messages := svc.GetMessages(c.SelectedChannel)
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)
}

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
import (
"github.com/erroneousboat/slack-term/src/views"
"log"
"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 (
@ -11,17 +16,30 @@ const (
)
type AppContext struct {
Body *termui.Grid
View *views.View
Mode string
Service *service.SlackService
Body *termui.Grid
View *views.View
Config *config.Config
Mode string
}
// TODO: arg Config
func CreateAppContext() *AppContext {
view := views.CreateChatView()
func CreateAppContext(flgConfig string) *AppContext {
// Load config
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{
View: view,
Mode: CommandMode,
Service: svc,
View: view,
Config: config,
Mode: CommandMode,
}
}

View File

@ -1,7 +1,10 @@
package handlers
import (
"fmt"
"github.com/gizak/termui"
"github.com/nlopes/slack"
"github.com/erroneousboat/slack-term/src/context"
"github.com/erroneousboat/slack-term/src/views"
@ -10,6 +13,28 @@ import (
func RegisterEventHandlers(ctx *context.AppContext) {
termui.Handle("/sys/kbd/", anyKeyHandler(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) {
@ -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
// will cause termui to panic
func actionResize(ctx *context.AppContext) {
@ -103,12 +133,23 @@ func actionQuit() {
func actionInsertMode(ctx *context.AppContext) {
ctx.Mode = context.InsertMode
ctx.View.Mode.Text = "INSERT"
ctx.View.Mode.Par.Text = "INSERT"
termui.Render(ctx.View.Mode)
}
func actionCommandMode(ctx *context.AppContext) {
ctx.Mode = context.CommandMode
ctx.View.Mode.Text = "NORMAL"
ctx.View.Mode.Par.Text = "NORMAL"
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
import (
"flag"
"log"
"os/user"
"path"
"github.com/erroneousboat/slack-term/src/context"
"github.com/erroneousboat/slack-term/src/handlers"
@ -8,16 +13,31 @@ import (
)
func main() {
// Start terminal user interface
err := termui.Init()
if err != nil {
panic(err)
}
defer termui.Close()
// create context
ctx := context.CreateAppContext()
// Get home dir for config file default
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.NewRow(
termui.NewCol(1, 0, ctx.View.Channels),
@ -31,9 +51,10 @@ func main() {
termui.Body.Align()
termui.Render(termui.Body)
// Set body in context
ctx.Body = termui.Body
// register handlers
// Register handlers
handlers.RegisterEventHandlers(ctx)
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 (
"github.com/erroneousboat/slack-term/src/components"
"github.com/erroneousboat/slack-term/src/service"
"github.com/gizak/termui"
)
@ -9,15 +10,15 @@ import (
type View struct {
Input *components.Input
Chat *components.Chat
Channels *termui.List
Mode *termui.Par
Channels *components.Channels
Mode *components.Mode
}
func CreateChatView() *View {
func CreateChatView(svc *service.SlackService) *View {
input := components.CreateInput()
channels := components.CreateChannelsComponent(input.Par.Height)
chat := components.CreateChat(input.Par.Height)
mode := components.CreateModeComponent()
channels := components.CreateChannels(svc, input.Par.Height)
chat := components.CreateChat(svc, input.Par.Height)
mode := components.CreateMode()
view := &View{
Input: input,