221 lines
5.0 KiB
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"`
|
||
|
}
|