website/pkg/geoip/geoip.go

140 lines
3.8 KiB
Go
Raw Normal View History

// Package geoip provides IP address geolocation features.
package geoip
import (
"errors"
"net"
"net/http"
"strings"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/utility"
"github.com/oschwald/geoip2-golang"
)
// GetRequestCity returns the GeoIP City result for the current HTTP request.
func GetRequestCity(r *http.Request) (*geoip2.City, error) {
var (
addr = utility.IPAddress(r)
ip = net.ParseIP(addr)
)
return GetCity(ip)
}
// GetRequestCountryFlag returns the country flag based on the current HTTP request IP address.
func GetRequestCountryFlag(r *http.Request) (string, error) {
city, err := GetRequestCity(r)
if err != nil {
// If the remote addr is localhost (local dev testing), default to US flag.
if addr := utility.IPAddress(r); addr == "127.0.0.1" || addr == "::1" {
return CountryFlagEmoji("US")
}
return "", err
}
return CountryFlagEmoji(city.Country.IsoCode)
}
// GetRequestCountryFlagWithCode returns the flag joined with the country code by a space (like CountryFlagEmojiWithCode).
func GetRequestCountryFlagWithCode(r *http.Request) (string, error) {
city, err := GetRequestCity(r)
if err != nil {
// If the remote addr is localhost (local dev testing), default to US flag.
if addr := utility.IPAddress(r); addr == "127.0.0.1" || addr == "::1" {
return CountryFlagEmojiWithCode("US")
}
return "", err
}
return CountryFlagEmojiWithCode(city.Country.IsoCode)
}
2023-08-16 03:56:22 +00:00
// GetChatFlagEmoji returns a specialized country flag emoji string for the BareRTC chat room.
//
// This will include the country flag emoji along with the country and territory/state name.
func GetChatFlagEmoji(r *http.Request) (string, error) {
city, err := GetRequestCity(r)
if err != nil {
// If the remote addr is localhost (local dev testing), default to US flag and only "US" code.
if addr := utility.IPAddress(r); addr == "127.0.0.1" || addr == "::1" {
return CountryFlagEmojiWithCode("US")
}
return "", err
}
// Codes to attach (state, country, etc.)
emoji, err := CountryFlagEmoji(city.Country.IsoCode)
if err != nil {
return emoji, err
}
2023-08-16 04:03:41 +00:00
// The components of text location part of the string.
var flags = []string{}
// The country. Name or ISO code?
if name, ok := city.Country.Names["en"]; ok {
2023-08-16 04:03:41 +00:00
flags = append(flags, name)
} else {
flags = append(flags, city.Country.IsoCode)
2023-08-16 03:56:22 +00:00
}
2023-08-16 04:03:41 +00:00
// Subdivisions (states)
2023-08-16 03:56:22 +00:00
if len(city.Subdivisions) > 0 {
// Stop at just one subdivision. This will be US states
// and general regions, but without getting too specific
// for UK users especially where the subdivisions can hone
// in on their city of 1,000 population!
sub := city.Subdivisions[0]
// Can we get its name?
if name, ok := sub.Names["en"]; ok {
flags = append(flags, name)
} else {
flags = append(flags, sub.IsoCode)
2023-08-16 03:56:22 +00:00
}
}
2023-08-16 04:03:41 +00:00
return emoji + " " + strings.Join(flags, ", "), nil
2023-08-16 03:56:22 +00:00
}
// GetCity queries the GeoIP database for city information for an IP address.
func GetCity(ip net.IP) (*geoip2.City, error) {
db, err := geoip2.Open(config.GeoIPPath)
if err != nil {
return nil, err
}
return db.City(ip)
}
// CountryFlagEmoji returns the emoji sequence for a country flag based on
// the two-letter country code.
func CountryFlagEmoji(alpha2 string) (string, error) {
if len(alpha2) != 2 {
return "", errors.New("country code must be two letters long")
}
alpha2 = strings.ToLower(alpha2)
var (
flagBaseIndex = '\U0001F1E6' - 'a'
box = func(ch byte) string {
return string(rune(ch) + flagBaseIndex)
}
)
return string(box(alpha2[0]) + box(alpha2[1])), nil
}
// CountryFlagEmojiWithCode returns a string consisting of the country flag, a space, and the alpha2 code passed in.
func CountryFlagEmojiWithCode(alpha2 string) (string, error) {
if emoji, err := CountryFlagEmoji(alpha2); err != nil {
return emoji, err
} else {
return emoji + " " + alpha2, nil
}
}