mirror of
https://github.com/thebaer/tildes.git
synced 2018-07-20 07:15:21 +00:00
List geographical locations of online users
This commit is contained in:
parent
f89a362a15
commit
feb0786090
32
templates/where.html
Normal file
32
templates/where.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{{define "where"}}
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<title>$ where</title>
|
||||||
|
|
||||||
|
<link type="text/css" rel="stylesheet" href="tilde.css" />
|
||||||
|
<style type="text/css">
|
||||||
|
div.user {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
div.user h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body id="hello" class="user-list">
|
||||||
|
|
||||||
|
<h1>$ where</h1> <span class="updated"><a href="/~bear/">↑ up</a></span>
|
||||||
|
|
||||||
|
<p>Last Updated: <time datetime="{{.Updated}}">{{.UpdatedForHumans}}</time></p>
|
||||||
|
|
||||||
|
{{range .Users}}
|
||||||
|
<div class="user">
|
||||||
|
<h2><a href="/~{{.Name}}/">~{{.Name}}</a></h2> <span class="location">{{Location .Region .Country}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
161
where/where.go
Normal file
161
where/where.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"io/ioutil"
|
||||||
|
"os/exec"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"net/http"
|
||||||
|
"flag"
|
||||||
|
"time"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Get arguments
|
||||||
|
outFilePtr := flag.String("f", "where", "Outputted HTML filename (without .html)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Get online users with `who`
|
||||||
|
users := who()
|
||||||
|
|
||||||
|
// Fetch user locations based on IP address
|
||||||
|
for i := range users {
|
||||||
|
getGeo(&users[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate page
|
||||||
|
generate(users, *outFilePtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Name string
|
||||||
|
IP string
|
||||||
|
Region string
|
||||||
|
Country string
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipRegex = regexp.MustCompile("(([0-9]{1,3}[.-]){3}[0-9]{1,3})")
|
||||||
|
|
||||||
|
func who() []User {
|
||||||
|
fmt.Println("who --ips")
|
||||||
|
|
||||||
|
cmd := exec.Command("who", "--ips")
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ips := make(map[string]string)
|
||||||
|
|
||||||
|
r := bufio.NewReader(stdout)
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
lineParts := strings.Split(scanner.Text(), " ")
|
||||||
|
name := lineParts[0]
|
||||||
|
|
||||||
|
fmt.Println(name)
|
||||||
|
|
||||||
|
// Extract IP address
|
||||||
|
ipMatch := ipRegex.FindAllString(scanner.Text(), 1)
|
||||||
|
if len(ipMatch) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize any host names with dashes
|
||||||
|
newIp := strings.Replace(ipMatch[0], "-", ".", -1)
|
||||||
|
|
||||||
|
ips[newIp] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
users := make([]User, len(ips))
|
||||||
|
i := 0
|
||||||
|
for ip, name := range ips {
|
||||||
|
users[i] = User{Name: name, IP: ip}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGeo(u *User) {
|
||||||
|
fmt.Printf("Fetching %s location...\n", u.Name)
|
||||||
|
|
||||||
|
response, err := http.Get(fmt.Sprintf("https://freegeoip.net/json/%s", u.IP))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
defer response.Body.Close()
|
||||||
|
contents, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dat map[string]interface{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(contents, &dat); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
region := dat["region_name"].(string)
|
||||||
|
country := dat["country_name"].(string)
|
||||||
|
|
||||||
|
u.Region = region
|
||||||
|
u.Country = country
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettyLocation(region, country string) string {
|
||||||
|
if region != "" {
|
||||||
|
return fmt.Sprintf("%s, %s", region, country)
|
||||||
|
}
|
||||||
|
return country
|
||||||
|
}
|
||||||
|
|
||||||
|
type Page struct {
|
||||||
|
Users []User
|
||||||
|
Updated string
|
||||||
|
UpdatedForHumans string
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate(users []User, outputFile string) {
|
||||||
|
fmt.Println("Generating page.")
|
||||||
|
|
||||||
|
f, err := os.Create(os.Getenv("HOME") + "/public_html/" + strings.ToLower(outputFile) + ".html")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
funcMap := template.FuncMap {
|
||||||
|
"Location": prettyLocation,
|
||||||
|
}
|
||||||
|
|
||||||
|
w := bufio.NewWriter(f)
|
||||||
|
template, err := template.New("").Funcs(funcMap).ParseFiles("../templates/where.html")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra page data
|
||||||
|
curTime := time.Now().UTC()
|
||||||
|
updatedReadable := curTime.Format(time.RFC1123)
|
||||||
|
updated := curTime.Format(time.RFC3339)
|
||||||
|
|
||||||
|
// Generate the page
|
||||||
|
page := &Page{Users: users, UpdatedForHumans: updatedReadable, Updated: updated}
|
||||||
|
template.ExecuteTemplate(w, "where", page)
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
|
fmt.Println("DONE!")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user