showdown/utils.go
// showdown2irc - use Showdown chat with an IRC client
// Copyright (C) 2016 Konrad Borowski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package showdown
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
"unicode"
"unicode/utf8"
)
// UserID represents a user ID. It's distinct from standard strings to
// prevent accidentally mistaking usernames with IDs
type UserID string
// RoomID represents a room ID.
type RoomID string
// SplitUser given a string with a rank and username provided User object
func SplitUser(name string) User {
auth, size := utf8.DecodeRuneInString(name)
return User{auth, name[size:]}
}
// ToID converts a username to its ID.
func ToID(name string) UserID {
var buffer bytes.Buffer
for _, character := range name {
character = unicode.ToLower(character)
if 'a' <= character && character <= 'z' || '0' <= character && character <= '9' {
buffer.WriteRune(character)
}
}
return UserID(buffer.String())
}
type serverDoesNotExistError struct{}
func (serverDoesNotExistError) Error() string {
return "Server does not exist"
}
var configurationRegexp = regexp.MustCompile(`(?m)^var config = (.*);$`)
type ServerAddress struct {
Host string
Port uint16
}
func findConfiguration(name string) (ServerAddress, error) {
if !strings.Contains(name, ".") {
name += ".psim.us"
}
serverConfiguration, err := downloadConfiguration(name)
if err != nil {
return ServerAddress{}, err
}
// Crossdomain API doesn't provide server information for main server.
if serverConfiguration.Host == "showdown" {
serverConfiguration.Host = "sim2.psim.us"
serverConfiguration.Port = 443
}
return serverConfiguration, nil
}
func downloadConfiguration(server string) (_ ServerAddress, err error) {
escapedName := url.QueryEscape(server)
res, err := http.Get("https://play.pokemonshowdown.com/crossdomain.php?host=" + escapedName)
if err != nil {
return
}
contents, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return
}
return parseConfiguration(contents)
}
func parseConfiguration(crossDomainOutput []byte) (serverConfiguration ServerAddress, err error) {
matches := configurationRegexp.FindSubmatch(crossDomainOutput)
if matches == nil {
err = new(serverDoesNotExistError)
return
}
jsonJSONData := matches[1]
var jsonData string
err = json.Unmarshal(jsonJSONData, &jsonData)
if err != nil {
return
}
err = json.Unmarshal([]byte(jsonData), &serverConfiguration)
return
}