Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.vscode
51 changes: 16 additions & 35 deletions README.md

Large diffs are not rendered by default.

175 changes: 87 additions & 88 deletions device_detector.go
Original file line number Diff line number Diff line change
@@ -1,97 +1,97 @@
package devicedetector

import (
"path/filepath"
"embed"
"path"
"strings"

regexp "github.com/dlclark/regexp2"
gover "github.com/mcuadros/go-version"

. "github.com/gamebtc/devicedetector/parser"
"github.com/gamebtc/devicedetector/parser/client"
"github.com/gamebtc/devicedetector/parser/device"
"github.com/slipros/devicedetector/parser"
"github.com/slipros/devicedetector/parser/client"
"github.com/slipros/devicedetector/parser/device"
)

//go:embed regexes/*
var EmbeddedRegexes embed.FS

const UNKNOWN = "UNK"
const VERSION = `3.12.5`
const VERSION = `6.4.7`

var desktopOsArray = []string{
`AmigaOS`,
`IBM`,
`GNU/Linux`,
`Mac`,
`Unix`,
`Windows`,
`BeOS`,
`Chrome OS`,
`AmigaOS`, `IBM`, `GNU/Linux`, `Mac`, `Unix`, `Windows`, `BeOS`, `Chrome OS`, `Chromium OS`,
}

var(
var (
chrMobReg = regexp.MustCompile(fixUserAgentRegEx(`Chrome/[\.0-9]* Mobile`), regexp.IgnoreCase)
chrTabReg = regexp.MustCompile(fixUserAgentRegEx(`Chrome/[\.0-9]* (?!Mobile)`), regexp.IgnoreCase)
opaTabReg = regexp.MustCompile(fixUserAgentRegEx(`Opera Tablet`), regexp.IgnoreCase)
opaTvReg = regexp.MustCompile(fixUserAgentRegEx(`Opera TV Store`), regexp.IgnoreCase)
opaTvReg = regexp.MustCompile(fixUserAgentRegEx(`Opera TV Store`), regexp.IgnoreCase)
)

func fixUserAgentRegEx(regex string) string {
reg := strings.ReplaceAll(regex, `/`, `\/`)
reg = strings.ReplaceAll(reg, `++`, `+`)

return `(?:^|[^A-Z_-])(?:` + reg + `)`
}

type DeviceDetector struct {
deviceParsers []device.DeviceParser
clientParsers []client.ClientParser
botParsers []BotParser
osParsers []OsParser
vendorParser *VendorFragments
botParsers []parser.BotParser
osParsers []parser.OsParser
vendorParser *parser.VendorFragments
DiscardBotInformation bool
SkipBotDetection bool
}

func NewDeviceDetector(dir string) (*DeviceDetector, error) {
vp, err := NewVendor(filepath.Join(dir, FixtureFileVendor))
func NewDeviceDetector() (*DeviceDetector, error) {
parser.ReadFile = EmbeddedRegexes.ReadFile

return newDeviceDetector("./regexes")
}

func newDeviceDetector(dir string) (*DeviceDetector, error) {
vp, err := parser.NewVendor(path.Join(dir, parser.FixtureFileVendor))
if err != nil {
return nil, err
}
osp, err := NewOss(filepath.Join(dir, FixtureFileOs))
osp, err := parser.NewOss(path.Join(dir, parser.FixtureFileOs))
if err != nil {
return nil, err
}

d := &DeviceDetector{
return &DeviceDetector{
vendorParser: vp,
osParsers: []OsParser{osp},
}

clientDir := filepath.Join(dir, "client")
d.clientParsers = client.NewClientParsers(clientDir,
[]string{
client.ParserNameFeedReader,
client.ParserNameMobileApp,
client.ParserNameMediaPlayer,
client.ParserNamePim,
client.ParserNameBrowser,
client.ParserNameLibrary,
})

deviceDir := filepath.Join(dir, "device")
d.deviceParsers = device.NewDeviceParsers(deviceDir,
[]string{
device.ParserNameHbbTv,
device.ParserNameConsole,
device.ParserNameCar,
device.ParserNameCamera,
device.ParserNamePortableMediaPlayer,
device.ParserNameMobile,
})

d.botParsers = []BotParser{
NewBot(filepath.Join(dir, FixtureFileBot)),
}


return d, nil
osParsers: []parser.OsParser{osp},
clientParsers: client.NewClientParsers(
path.Join(dir, "client"),
[]string{
client.ParserNameFeedReader,
client.ParserNameMobileApp,
client.ParserNameMediaPlayer,
client.ParserNamePim,
client.ParserNameBrowser,
client.ParserNameLibrary,
},
),
deviceParsers: device.NewDeviceParsers(
path.Join(dir, "device"),
[]string{
device.ParserNameHbbTv,
device.ParserNameConsole,
device.ParserNameCar,
device.ParserNameCamera,
device.ParserNamePortableMediaPlayer,
device.ParserNameMobile,
},
),
botParsers: []parser.BotParser{
parser.NewBot(path.Join(dir, parser.FixtureFileBot)),
},
}, nil
}

func (d *DeviceDetector) AddClientParser(cp client.ClientParser) {
Expand All @@ -110,27 +110,27 @@ func (d *DeviceDetector) GetDeviceParsers() []device.DeviceParser {
return d.deviceParsers
}

func (d *DeviceDetector) AddBotParser(op BotParser) {
func (d *DeviceDetector) AddBotParser(op parser.BotParser) {
d.botParsers = append(d.botParsers, op)
}

func (d *DeviceDetector) GetBotParsers() []BotParser {
func (d *DeviceDetector) GetBotParsers() []parser.BotParser {
return d.botParsers
}

func (d *DeviceDetector) ParseBot(ua string) *BotMatchResult {
func (d *DeviceDetector) ParseBot(ua string) *parser.BotMatchResult {
if !d.SkipBotDetection {
for _, parser := range d.botParsers {
parser.DiscardDetails(d.DiscardBotInformation)
if r := parser.Parse(ua); r != nil {
for _, p := range d.botParsers {
p.DiscardDetails(d.DiscardBotInformation)
if r := p.Parse(ua); r != nil {
return r
}
}
}
return nil
}

func (d *DeviceDetector) ParseOs(ua string) *OsMatchResult {
func (d *DeviceDetector) ParseOs(ua string) *parser.OsMatchResult {
for _, p := range d.osParsers {
if r := p.Parse(ua); r != nil {
return r
Expand All @@ -140,17 +140,17 @@ func (d *DeviceDetector) ParseOs(ua string) *OsMatchResult {
}

func (d *DeviceDetector) ParseClient(ua string) *client.ClientMatchResult {
for _, parser := range d.clientParsers {
if r := parser.Parse(ua); r != nil {
for _, p := range d.clientParsers {
if r := p.Parse(ua); r != nil {
return r
}
}
return nil
}

func (d *DeviceDetector) ParseDevice(ua string) *device.DeviceMatchResult {
for _, parser := range d.deviceParsers {
if r := parser.Parse(ua); r != nil {
for _, p := range d.deviceParsers {
if r := p.Parse(ua); r != nil {
return r
}
}
Expand All @@ -171,80 +171,80 @@ func (d *DeviceDetector) parseInfo(info *DeviceInfo) {

os := info.GetOs()
osShortName := os.ShortName
osFamily := GetOsFamily(osShortName)
osFamily := parser.GetOsFamily(osShortName)
osVersion := os.Version
cmr := info.GetClient()

if info.Brand == "" && (osShortName == `ATV` || osShortName == `IOS` || osShortName == `MAC`) {
info.Brand = `AP`
}

deviceType := GetDeviceType(info.Type)
deviceType := parser.GetDeviceType(info.Type)
// Chrome on Android passes the device type based on the keyword 'Mobile'
// If it is present the device should be a smartphone, otherwise it's a tablet
// See https://developer.chrome.com/multidevice/user-agent#chrome_for_android_user_agent
if deviceType == DEVICE_TYPE_INVALID && osFamily == `Android` {
if browserName,ok:=client.GetBrowserFamily(cmr.ShortName); ok&&browserName== `Chrome` {
if deviceType == parser.DEVICE_TYPE_INVALID && osFamily == `Android` {
if browserName, ok := client.GetBrowserFamily(cmr.ShortName); ok && browserName == `Chrome` {
if ok, _ := chrMobReg.MatchString(ua); ok {
deviceType = DEVICE_TYPE_SMARTPHONE
deviceType = parser.DEVICE_TYPE_SMARTPHONE
} else if ok, _ = chrTabReg.MatchString(ua); ok {
deviceType = DEVICE_TYPE_TABLET
deviceType = parser.DEVICE_TYPE_TABLET
}
}
}

if deviceType == DEVICE_TYPE_INVALID {
if deviceType == parser.DEVICE_TYPE_INVALID {
if info.HasAndroidMobileFragment() {
deviceType = DEVICE_TYPE_TABLET
deviceType = parser.DEVICE_TYPE_TABLET
} else if ok, _ := opaTabReg.MatchString(ua); ok {
deviceType = DEVICE_TYPE_TABLET
deviceType = parser.DEVICE_TYPE_TABLET
} else if info.HasAndroidMobileFragment() {
deviceType = DEVICE_TYPE_SMARTPHONE
deviceType = parser.DEVICE_TYPE_SMARTPHONE
} else if osShortName == "AND" && osVersion != "" {
if gover.CompareSimple(osVersion, `2.0`) == -1 {
deviceType = DEVICE_TYPE_SMARTPHONE
deviceType = parser.DEVICE_TYPE_SMARTPHONE
} else if gover.CompareSimple(osVersion, `3.0`) >= 0 &&
gover.CompareSimple(osVersion, `4.0`) == -1 {
deviceType = DEVICE_TYPE_TABLET
deviceType = parser.DEVICE_TYPE_TABLET
}
}
}

// All detected feature phones running android are more likely a smartphone
if deviceType == DEVICE_TYPE_FEATURE_PHONE && osFamily == `Android` {
deviceType = DEVICE_TYPE_SMARTPHONE
if deviceType == parser.DEVICE_TYPE_FEATURE_PHONE && osFamily == `Android` {
deviceType = parser.DEVICE_TYPE_SMARTPHONE
}

// According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
if deviceType == DEVICE_TYPE_INVALID &&
if deviceType == parser.DEVICE_TYPE_INVALID &&
(osShortName == `WRT` || (osShortName == `WIN` && gover.CompareSimple(osVersion, `8`) >= 0)) &&
info.IsTouchEnabled() {
deviceType = DEVICE_TYPE_TABLET
deviceType = parser.DEVICE_TYPE_TABLET
}

// All devices running Opera TV Store are assumed to be a tv
if ok, _ := opaTvReg.MatchString(ua); ok {
deviceType = DEVICE_TYPE_TV
deviceType = parser.DEVICE_TYPE_TV
}

// Devices running Kylo or Espital TV Browsers are assumed to be a TV
if deviceType == DEVICE_TYPE_INVALID {
if deviceType == parser.DEVICE_TYPE_INVALID {
if cmr.Name == `Kylo` || cmr.Name == `Espial TV Browser` {
deviceType = DEVICE_TYPE_TV
deviceType = parser.DEVICE_TYPE_TV
} else if info.IsDesktop() {
deviceType = DEVICE_TYPE_DESKTOP
deviceType = parser.DEVICE_TYPE_DESKTOP
}
}

if deviceType != DEVICE_TYPE_INVALID {
info.Type = GetDeviceName(deviceType)
if deviceType != parser.DEVICE_TYPE_INVALID {
info.Type = parser.GetDeviceName(deviceType)
}
return
}

func (d *DeviceDetector) Parse(ua string) *DeviceInfo {
// skip parsing for empty useragents or those not containing any letter
if !StringContainsLetter(ua) {
if !parser.StringContainsLetter(ua) {
return nil
}

Expand All @@ -268,4 +268,3 @@ func (d *DeviceDetector) Parse(ua string) *DeviceInfo {

return info
}

12 changes: 6 additions & 6 deletions device_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package devicedetector
import (
regexp "github.com/dlclark/regexp2"

. "github.com/gamebtc/devicedetector/parser"
"github.com/gamebtc/devicedetector/parser/client"
"github.com/gamebtc/devicedetector/parser/device"
. "github.com/slipros/devicedetector/parser"
"github.com/slipros/devicedetector/parser/client"
"github.com/slipros/devicedetector/parser/device"
)

var (
Expand All @@ -17,9 +17,9 @@ var (
type DeviceInfo struct {
userAgent string
device.DeviceMatchResult
client *client.ClientMatchResult
os *OsMatchResult
bot *BotMatchResult
client *client.ClientMatchResult
os *OsMatchResult
bot *BotMatchResult
}

func (d *DeviceInfo) GetDeviceType() int {
Expand Down
Loading