Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Check all 2 letter twitter usernames for availability
package twitterfindname
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
type HttpResponse struct {
url string
name string
nameresp UsernameLookup
valid bool
response *http.Response
err error
}
type UsernameLookup struct {
// Reason: for twitter username: available, taken
Reason string `json:"reason"`
// Message: Message about selected username
Message string `json:"msg"`
// Valid: Is this a valid username?
Valid bool `json:"valid"`
// Description: More info about the chosen username
Description string `json:"desc"`
}
// Given a JSON string return if the serialized data says is Available
func TwitterNameAvailable(s string) (bool, UsernameLookup, error) {
// See docs: http://golang.org/pkg/encoding/json/#example_Decoder
dec := json.NewDecoder(strings.NewReader(s))
var m UsernameLookup
if err := dec.Decode(&m); err != nil {
return false, UsernameLookup{}, err
}
return m.Valid, m, nil
}
func FindTwitterUsernames() {
names := generate2letterUsernames()
// Split the number of concurrent dispatches into chunks
// This was necessary on my local machine b/c dispatching all resulted in:
// panic: runtime error: invalid memory address or nil pointer dereference
// [signal 0xb code=0x1 addr=0x38 pc=0x26399]
// goroutine 257 [running]:...
// Note: ^ interesting that it's ~256-ish...
splitby := 200
splitnames := names
for i := 0; i*splitby < len(names); i++ {
if (i+1)*splitby > len(names) {
splitnames = names[(i * splitby):len(names)]
} else {
splitnames = names[(i * splitby):((i + 1) * splitby)]
}
results := asyncHttpGetsValidate(splitnames)
for _ = range splitnames {
result := <-results
fmt.Printf("%t: %s - %s\n", result.valid, result.name, result.nameresp)
}
}
}
func generate2letterUsernames() []string {
validchars := "0123456789abcdefghijklmnopqrstuvwxyz_"
var result []string
for _, firstchar := range validchars {
for _, secondchar := range validchars {
result = append(result, string(firstchar)+string(secondchar))
}
}
return result
}
func asyncHttpGetsValidate(names []string) <-chan *HttpResponse {
// Originally from: https://gist.github.com/4013851
ch := make(chan *HttpResponse, len(names)) // buffered
for _, name := range names {
go func(name string) {
// Lookup if the twitter username is available
url := "https://twitter.com/users/username_available?username=" + name
fmt.Printf("Fetching username: %s \n", name)
resp, err := http.Get(url)
body, err := ioutil.ReadAll(resp.Body)
valid, usernamejson, err := TwitterNameAvailable(string(body))
resp.Body.Close()
ch <- &HttpResponse{url, name, usernamejson, valid, resp, err}
}(name)
}
return ch
}
package twitterfindname
import "testing"
func TestTwitterNameAvailable(t *testing.T) {
var tests = []struct {
valid bool
name, response string
}{
{false, "☃", `{"reason":"improper_format","msg":"Only use letters, numbers and '_'","valid":false,"desc":"Your username can only contain letters, numbers and '_'"}`},
{true, "thisnameisnottaken", `{"reason":"available","msg":"Available!","valid":true,"desc":"Available!"}`},
{false, "a", `{"reason":"taken","msg":"Username has already been taken","valid":false,"desc":"That username has been taken. Please choose another."}`},
}
for _, c := range tests {
valid, _, _ := TwitterNameAvailable(c.response)
if valid != c.valid {
t.Errorf("TwitterNameAvailable(%q) should be %t", c.name, c.valid)
}
}
}
func TestFindTwitterUsername(t *testing.T) {
if len(generate2letterUsernames()) != 1369 {
t.Errorf("generate2letterUsernames")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment