Skip to content

Instantly share code, notes, and snippets.

@dchapes
Last active August 29, 2015 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dchapes/a29933d2bf5eec443b5e to your computer and use it in GitHub Desktop.
Save dchapes/a29933d2bf5eec443b5e to your computer and use it in GitHub Desktop.
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
}
@dchapes
Copy link
Author

dchapes commented Apr 25, 2015

An experiment with using a gist for a codereview.stackexchange.com.

Pros:

  • Changes can be made and observed one at a time instead of all at once.
  • Clone-able DVCS of said changes is available (e.g. with git clone https://gist.github.com/a29933d2bf5eec443b5e.git stack-chat).

Cons:

  • Although gists can be git cloned they don't appear to be go get-able.
  • The gist webview doesn't seem to show commit messages.
  • The gist webview doesn't appear to allow for line comments (like on regular GitHub commits).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment