| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- // Copyright (c) 2021 Tulir Asokan
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- // Package types contains various structs and other types used by whatsmeow.
- package types
- import (
- "database/sql"
- "database/sql/driver"
- "fmt"
- "regexp"
- "strconv"
- "strings"
- signalProtocol "go.mau.fi/libsignal/protocol"
- )
- // Known JID servers on WhatsApp
- const (
- DefaultUserServer = "s.whatsapp.net"
- GroupServer = "g.us"
- LegacyUserServer = "c.us"
- BroadcastServer = "broadcast"
- HiddenUserServer = "lid"
- MessengerServer = "msgr"
- InteropServer = "interop"
- NewsletterServer = "newsletter"
- HostedServer = "hosted"
- HostedLIDServer = "hosted.lid"
- BotServer = "bot"
- )
- // Some JIDs that are contacted often.
- var (
- EmptyJID = JID{}
- GroupServerJID = NewJID("", GroupServer)
- ServerJID = NewJID("", DefaultUserServer)
- BroadcastServerJID = NewJID("", BroadcastServer)
- StatusBroadcastJID = NewJID("status", BroadcastServer)
- LegacyPSAJID = NewJID("0", LegacyUserServer)
- PSAJID = NewJID("0", DefaultUserServer)
- OfficialBusinessJID = NewJID("16505361212", LegacyUserServer)
- MetaAIJID = NewJID("13135550002", DefaultUserServer)
- NewMetaAIJID = NewJID("867051314767696", BotServer)
- )
- var (
- WhatsAppDomain = uint8(0) // This is the main domain type that whatsapp uses
- LIDDomain = uint8(1) // This is the domain for LID type JIDs
- HostedDomain = uint8(128) // This is the domain for Hosted type JIDs
- HostedLIDDomain = uint8(129) // This is the domain for Hosted LID type JIDs
- )
- // MessageID is the internal ID of a WhatsApp message.
- type MessageID = string
- // MessageServerID is the server ID of a WhatsApp newsletter message.
- type MessageServerID = int
- // JID represents a WhatsApp user ID.
- //
- // There are two types of JIDs: regular JID pairs (user and server) and AD-JIDs (user, agent and device).
- // AD JIDs are only used to refer to specific devices of users, so the server is always s.whatsapp.net (DefaultUserServer).
- // Regular JIDs can be used for entities on any servers (users, groups, broadcasts).
- type JID struct {
- User string
- RawAgent uint8
- Device uint16
- Integrator uint16
- Server string
- }
- func (jid JID) ActualAgent() uint8 {
- switch jid.Server {
- case DefaultUserServer:
- return WhatsAppDomain
- case HiddenUserServer:
- return LIDDomain
- case HostedServer:
- return HostedDomain
- case HostedLIDServer:
- return HostedLIDDomain
- default:
- return jid.RawAgent
- }
- }
- // UserInt returns the user as an integer. This is only safe to run on normal users, not on groups or broadcast lists.
- func (jid JID) UserInt() uint64 {
- number, _ := strconv.ParseUint(jid.User, 10, 64)
- return number
- }
- // ToNonAD returns a version of the JID struct that doesn't have the agent and device set.
- func (jid JID) ToNonAD() JID {
- return JID{
- User: jid.User,
- Server: jid.Server,
- Integrator: jid.Integrator,
- }
- }
- // SignalAddress returns the Signal protocol address for the user.
- func (jid JID) SignalAddress() *signalProtocol.SignalAddress {
- return signalProtocol.NewSignalAddress(jid.SignalAddressUser(), uint32(jid.Device))
- }
- func (jid JID) SignalAddressUser() string {
- user := jid.User
- agent := jid.ActualAgent()
- if agent != 0 {
- user = fmt.Sprintf("%s_%d", jid.User, agent)
- }
- return user
- }
- // IsBroadcastList returns true if the JID is a broadcast list, but not the status broadcast.
- func (jid JID) IsBroadcastList() bool {
- return jid.Server == BroadcastServer && jid.User != StatusBroadcastJID.User
- }
- var botUserRegex = regexp.MustCompile(`^1313555\d{4}$|^131655500\d{2}$`)
- func (jid JID) IsBot() bool {
- return (jid.Server == DefaultUserServer && botUserRegex.MatchString(jid.User) && jid.Device == 0) || jid.Server == BotServer
- }
- // NewADJID creates a new AD JID.
- func NewADJID(user string, agent, device uint8) JID {
- var server string
- // agent terminology isn't 100% correct here, these are the domainType, but whatsapp usually places them in the same place (if the switch case below doesn't process it, then it is an agent instead)
- switch agent {
- case LIDDomain:
- server = HiddenUserServer
- agent = 0
- case HostedDomain:
- server = HostedServer
- agent = 0
- case HostedLIDDomain:
- server = HostedLIDServer
- agent = 0
- default:
- case WhatsAppDomain:
- server = DefaultUserServer // will just default to the normal server
- }
- return JID{
- User: user,
- RawAgent: agent,
- Device: uint16(device),
- Server: server,
- }
- }
- // ParseJID parses a JID out of the given string. It supports both regular and AD JIDs.
- func ParseJID(jid string) (JID, error) {
- parts := strings.Split(jid, "@")
- if len(parts) == 1 {
- return NewJID("", parts[0]), nil
- }
- parsedJID := JID{User: parts[0], Server: parts[1]}
- if strings.ContainsRune(parsedJID.User, '.') {
- parts = strings.Split(parsedJID.User, ".")
- if len(parts) != 2 {
- return parsedJID, fmt.Errorf("unexpected number of dots in JID")
- }
- parsedJID.User = parts[0]
- ad := parts[1]
- parts = strings.Split(ad, ":")
- if len(parts) > 2 {
- return parsedJID, fmt.Errorf("unexpected number of colons in JID")
- }
- agent, err := strconv.Atoi(parts[0])
- if err != nil {
- return parsedJID, fmt.Errorf("failed to parse device from JID: %w", err)
- }
- parsedJID.RawAgent = uint8(agent)
- if len(parts) == 2 {
- device, err := strconv.Atoi(parts[1])
- if err != nil {
- return parsedJID, fmt.Errorf("failed to parse device from JID: %w", err)
- }
- parsedJID.Device = uint16(device)
- }
- } else if strings.ContainsRune(parsedJID.User, ':') {
- parts = strings.Split(parsedJID.User, ":")
- if len(parts) != 2 {
- return parsedJID, fmt.Errorf("unexpected number of colons in JID")
- }
- parsedJID.User = parts[0]
- device, err := strconv.Atoi(parts[1])
- if err != nil {
- return parsedJID, fmt.Errorf("failed to parse device from JID: %w", err)
- }
- parsedJID.Device = uint16(device)
- }
- return parsedJID, nil
- }
- // NewJID creates a new regular JID.
- func NewJID(user, server string) JID {
- return JID{
- User: user,
- Server: server,
- }
- }
- func (jid JID) ADString() string {
- return fmt.Sprintf("%s.%d:%d@%s", jid.User, jid.RawAgent, jid.Device, jid.Server)
- }
- // String converts the JID to a string representation.
- // The output string can be parsed with ParseJID.
- func (jid JID) String() string {
- if jid.RawAgent > 0 {
- return fmt.Sprintf("%s.%d:%d@%s", jid.User, jid.RawAgent, jid.Device, jid.Server)
- } else if jid.Device > 0 {
- return fmt.Sprintf("%s:%d@%s", jid.User, jid.Device, jid.Server)
- } else if len(jid.User) > 0 {
- return fmt.Sprintf("%s@%s", jid.User, jid.Server)
- } else {
- return jid.Server
- }
- }
- // MarshalText implements encoding.TextMarshaler for JID
- func (jid JID) MarshalText() ([]byte, error) {
- return []byte(jid.String()), nil
- }
- // UnmarshalText implements encoding.TextUnmarshaler for JID
- func (jid *JID) UnmarshalText(val []byte) error {
- out, err := ParseJID(string(val))
- if err != nil {
- return err
- }
- *jid = out
- return nil
- }
- // IsEmpty returns true if the JID has no server (which is required for all JIDs).
- func (jid JID) IsEmpty() bool {
- return len(jid.Server) == 0
- }
- var _ sql.Scanner = (*JID)(nil)
- // Scan scans the given SQL value into this JID.
- func (jid *JID) Scan(src interface{}) error {
- if src == nil {
- return nil
- }
- var out JID
- var err error
- switch val := src.(type) {
- case string:
- out, err = ParseJID(val)
- case []byte:
- out, err = ParseJID(string(val))
- default:
- err = fmt.Errorf("unsupported type %T for scanning JID", val)
- }
- if err != nil {
- return err
- }
- *jid = out
- return nil
- }
- // Value returns the string representation of the JID as a value that the SQL package can use.
- func (jid JID) Value() (driver.Value, error) {
- if len(jid.Server) == 0 {
- return nil, nil
- }
- return jid.String(), nil
- }
|