slacktricks/gethistory/gethistory.go

221 lines
5.0 KiB
Go

// © 2018 the slacktricks Authors under the WTFPL license. See AUTHORS for the list of authors.
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strconv"
)
var (
token = flag.String("token", "", "Slack API token")
channel = flag.String("channel", "", "Slack channel ID")
limit = flag.Int("limit", 100, "Number of items to return")
byChannel = flag.Bool("byChannel", false, "Arrange data by channel (default by user)")
)
func main() {
flag.Parse()
users := getUsers()
history := getHistory(*channel)
if !*byChannel {
for _, u := range users.Members {
writeUser(history, u)
}
} else {
writeChannel(history)
}
}
func getUsers() userResp {
users := userResp{}
log.Printf("Getting users")
body := mkReq("https://slack.com/api/users.list",
"token", *token,
)
err := json.Unmarshal(body, &users)
checkErr(err)
return users
}
func getHistory(ch string) []histResp {
history := []histResp{}
cursor := ""
for {
log.Printf("Getting history cursor: '%s'", cursor)
body := mkReq("https://slack.com/api/conversations.history",
"token", *token,
"channel", ch,
"limit", strconv.Itoa(*limit),
"cursor", cursor,
)
resp := histResp{}
err := json.Unmarshal(body, &resp)
checkErr(err)
history = append(history, resp)
if resp.ResponseMetadata.NextCursor == "" {
break
} else {
cursor = resp.ResponseMetadata.NextCursor
}
}
return history
}
func checkErr(err error) {
if err != nil {
log.Fatal(err)
}
}
func mkReq(path string, arg ...string) []byte {
if len(arg)%2 != 0 {
log.Fatal("Bad request arg number.")
}
u, err := url.Parse(path)
checkErr(err)
q := u.Query()
for i := 0; i < len(arg); i += 2 {
key := arg[i]
val := arg[i+1]
if val != "" {
q.Set(key, val)
}
}
u.RawQuery = q.Encode()
resp, err := http.Get(u.String())
checkErr(err)
body, err := ioutil.ReadAll(resp.Body)
checkErr(err)
return body
}
func writeChannel(history []histResp) {
fname := *channel + ".json"
f, err := os.Create(fname)
checkErr(err)
defer f.Close()
j, err := json.Marshal(history)
checkErr(err)
f.Write(j)
reactionCount := map[string]int{}
for _, h := range history {
for _, m := range h.Messages {
if len(m.Reactions) > 0 {
for _, r := range m.Reactions {
reactionCount[r.Name] += r.Count
}
}
}
}
fmt.Printf("%+v", reactionCount)
}
func writeUser(history []histResp, u user) {
// open file and write shit out
fname := u.Name + ".txt"
id := u.ID
f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0755)
checkErr(err)
defer f.Close()
for _, h := range history {
for _, m := range h.Messages {
if m.User == id {
fmt.Fprintf(f, "%s\n", m.Text)
}
}
}
}
type histResp struct {
Ok bool `json:"ok"`
Messages []struct {
Type string `json:"type"`
User string `json:"user"`
Text string `json:"text"`
Ts string `json:"ts"`
Reactions []reaction `json:"reactions"`
} `json:"messages"`
HasMore bool `json:"has_more"`
PinCount int `json:"pin_count"`
ResponseMetadata struct {
NextCursor string `json:"next_cursor"`
} `json:"response_metadata"`
}
type reaction struct {
Name string `json:"name"`
Users []string `json:"users"`
Count int `json:"count"`
}
type user struct {
ID string `json:"id"`
TeamID string `json:"team_id"`
Name string `json:"name"`
Deleted bool `json:"deleted"`
Color string `json:"color"`
RealName string `json:"real_name"`
Tz string `json:"tz"`
TzLabel string `json:"tz_label"`
TzOffset int `json:"tz_offset"`
Profile struct {
AvatarHash string `json:"avatar_hash"`
StatusText string `json:"status_text"`
StatusEmoji string `json:"status_emoji"`
RealName string `json:"real_name"`
DisplayName string `json:"display_name"`
RealNameNormalized string `json:"real_name_normalized"`
DisplayNameNormalized string `json:"display_name_normalized"`
Email string `json:"email"`
Image24 string `json:"image_24"`
Image32 string `json:"image_32"`
Image48 string `json:"image_48"`
Image72 string `json:"image_72"`
Image192 string `json:"image_192"`
Image512 string `json:"image_512"`
Team string `json:"team"`
} `json:"profile"`
IsAdmin bool `json:"is_admin"`
IsOwner bool `json:"is_owner"`
IsPrimaryOwner bool `json:"is_primary_owner"`
IsRestricted bool `json:"is_restricted"`
IsUltraRestricted bool `json:"is_ultra_restricted"`
IsBot bool `json:"is_bot"`
Updated int `json:"updated"`
IsAppUser bool `json:"is_app_user,omitempty"`
Has2Fa bool `json:"has_2fa"`
}
type userResp struct {
Ok bool `json:"ok"`
Members []user `json:"members"`
CacheTs int `json:"cache_ts"`
ResponseMetadata struct {
NextCursor string `json:"next_cursor"`
} `json:"response_metadata"`
}