-
-
Save dchapes/a29933d2bf5eec443b5e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"flag" | |
"fmt" | |
"html" | |
"log" | |
"net/http" | |
"net/url" | |
"os" | |
"strconv" | |
"time" | |
"github.com/kennygrant/sanitize" | |
) | |
type ChatEvent struct { | |
EventType int8 `json:"event_type"` | |
Timestamp setime `json:"time_stamp"` | |
Content string `json:"content"` | |
UserID int64 `json:"user_id"` | |
Username string `json:"user_name"` | |
MessageID int64 `json:"message_id"` | |
ParentID int64 `json:"parent_id"` | |
ShowParent bool `json:"show_parent"` | |
MessageEdits int8 `json:"message_edits"` | |
MessageStars int8 `json:"message_stars"` | |
} | |
type ChatEvents struct { | |
Events []ChatEvent | |
Time int64 | |
Sync int64 | |
Ms int8 | |
} | |
const ( | |
mode = "Messages" | |
chatURL = "http://chat.stackexchange.com/chats/8595/events" | |
// Never use this value directly: SE only uses it as a hint | |
// and might return a bit more when it feels like it | |
messageCount = 100 | |
since = 0 | |
) | |
var ( | |
lastMessage time.Time | |
fkey = flag.String("fkey", "", `StackExchange chat "fkey"`) | |
ignoreUsers = map[int64]bool{ | |
-263: true, // "Captain Obvious" | |
125580: true, // "Duga" | |
} | |
) | |
func main() { | |
flag.Parse() | |
if *fkey == "" { | |
fmt.Fprintln(os.Stderr, `missing required -fkey argument | |
Go to the chat, F12, reload, look at the /events call | |
and steal its fkey property`) | |
os.Exit(2) // 2 to match flag's exit code | |
} | |
for range time.Tick(time.Second) { | |
parsedJSON, err := fetchMessages() | |
if err != nil { | |
log.Println("fetching messages:", err) | |
// TODO, abort or backoff on repeated failures? | |
continue | |
} | |
newestMessages := getNewestMessages(parsedJSON.Events) | |
for _, msg := range newestMessages { | |
if ignoreUsers[msg.UserID] { | |
continue | |
} | |
fmt.Printf("[%v] %s: %s\n", | |
msg.Timestamp.Format("15:04:05"), | |
msg.Username, | |
msg.Content) | |
} | |
} | |
} | |
func fetchMessages() (ChatEvents, error) { | |
var parsedJSON ChatEvents | |
resp, err := http.PostForm(chatURL, url.Values{ | |
"mode": {mode}, | |
"msgCount": {strconv.Itoa(messageCount)}, | |
"fkey": {*fkey}, | |
"since": {strconv.Itoa(since)}, | |
}) | |
if err != nil { | |
return parsedJSON, err | |
} | |
defer resp.Body.Close() | |
dec := json.NewDecoder(resp.Body) | |
err = dec.Decode(&parsedJSON) | |
return parsedJSON, err | |
} | |
func getNewestMessages(events []ChatEvent) []ChatEvent { | |
output := make([]ChatEvent, 0) | |
for _, e := range events { | |
if e.Timestamp.After(lastMessage) { | |
e.Content = html.UnescapeString(sanitize.HTML(e.Content)) | |
output = append(output, e) | |
lastMessage = e.Timestamp.Time | |
} | |
} | |
return output | |
} | |
// setime is an unmarshable StackExchange timestamp or date. | |
// See https://api.stackexchange.com/docs/dates | |
type setime struct{ time.Time } | |
func (t *setime) UnmarshalJSON(b []byte) error { | |
var seconds int64 | |
if err := json.Unmarshal(b, &seconds); err != nil { | |
return err | |
} | |
t.Time = time.Unix(seconds, 0) | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
An experiment with using a gist for a codereview.stackexchange.com.
Pros:
git clone https://gist.github.com/a29933d2bf5eec443b5e.git stack-chat
).Cons:
git clone
d they don't appear to bego get
-able.