mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-14 17:07:29 +08:00
160 lines
4.0 KiB
Go
160 lines
4.0 KiB
Go
package channels
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type WhatsAppBridgeStatus struct {
|
|
State string `json:"state"`
|
|
Connected bool `json:"connected"`
|
|
LoggedIn bool `json:"logged_in"`
|
|
BridgeAddr string `json:"bridge_addr"`
|
|
UserJID string `json:"user_jid,omitempty"`
|
|
PushName string `json:"push_name,omitempty"`
|
|
Platform string `json:"platform,omitempty"`
|
|
QRCode string `json:"qr_code,omitempty"`
|
|
QRAvailable bool `json:"qr_available"`
|
|
LastEvent string `json:"last_event,omitempty"`
|
|
LastError string `json:"last_error,omitempty"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
InboundCount int `json:"inbound_count"`
|
|
OutboundCount int `json:"outbound_count"`
|
|
ReadReceiptCount int `json:"read_receipt_count"`
|
|
LastInboundAt string `json:"last_inbound_at,omitempty"`
|
|
LastOutboundAt string `json:"last_outbound_at,omitempty"`
|
|
LastReadAt string `json:"last_read_at,omitempty"`
|
|
LastInboundFrom string `json:"last_inbound_from,omitempty"`
|
|
LastOutboundTo string `json:"last_outbound_to,omitempty"`
|
|
LastInboundText string `json:"last_inbound_text,omitempty"`
|
|
LastOutboundText string `json:"last_outbound_text,omitempty"`
|
|
}
|
|
|
|
func ParseWhatsAppBridgeListenAddr(raw string) (string, error) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return "", fmt.Errorf("bridge url is required")
|
|
}
|
|
if strings.Contains(raw, "://") {
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return "", fmt.Errorf("parse bridge url: %w", err)
|
|
}
|
|
if strings.TrimSpace(u.Host) == "" {
|
|
return "", fmt.Errorf("bridge url host is required")
|
|
}
|
|
return u.Host, nil
|
|
}
|
|
return raw, nil
|
|
}
|
|
|
|
func BridgeStatusURL(raw string) (string, error) {
|
|
return bridgeEndpointURL(raw, "status")
|
|
}
|
|
|
|
func BridgeLogoutURL(raw string) (string, error) {
|
|
return bridgeEndpointURL(raw, "logout")
|
|
}
|
|
|
|
func bridgeEndpointURL(raw, endpoint string) (string, error) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return "", fmt.Errorf("bridge url is required")
|
|
}
|
|
if !strings.Contains(raw, "://") {
|
|
raw = "ws://" + raw
|
|
}
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return "", fmt.Errorf("parse bridge url: %w", err)
|
|
}
|
|
switch u.Scheme {
|
|
case "wss":
|
|
u.Scheme = "https"
|
|
default:
|
|
u.Scheme = "http"
|
|
}
|
|
u.Path = bridgeSiblingPath(u.Path, endpoint)
|
|
u.RawQuery = ""
|
|
u.Fragment = ""
|
|
return u.String(), nil
|
|
}
|
|
|
|
func bridgeSiblingPath(pathValue, endpoint string) string {
|
|
pathValue = strings.TrimSpace(pathValue)
|
|
if endpoint == "" {
|
|
endpoint = "status"
|
|
}
|
|
if pathValue == "" || pathValue == "/" {
|
|
return "/" + endpoint
|
|
}
|
|
trimmed := strings.TrimSuffix(pathValue, "/")
|
|
if strings.HasSuffix(trimmed, "/ws") {
|
|
return strings.TrimSuffix(trimmed, "/ws") + "/" + endpoint
|
|
}
|
|
return trimmed + "/" + endpoint
|
|
}
|
|
|
|
func normalizeBridgeBasePath(basePath string) string {
|
|
basePath = strings.TrimSpace(basePath)
|
|
if basePath == "" || basePath == "/" {
|
|
return "/"
|
|
}
|
|
if !strings.HasPrefix(basePath, "/") {
|
|
basePath = "/" + basePath
|
|
}
|
|
return strings.TrimSuffix(basePath, "/")
|
|
}
|
|
|
|
func joinBridgeRoute(basePath, endpoint string) string {
|
|
basePath = normalizeBridgeBasePath(basePath)
|
|
if basePath == "/" {
|
|
return "/" + strings.TrimPrefix(endpoint, "/")
|
|
}
|
|
return basePath + "/" + strings.TrimPrefix(endpoint, "/")
|
|
}
|
|
|
|
func isLocalRequest(r *http.Request) bool {
|
|
if r == nil {
|
|
return false
|
|
}
|
|
addrs, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return isLocalRemoteAddr(strings.TrimSpace(r.RemoteAddr), addrs)
|
|
}
|
|
|
|
func isLocalRemoteAddr(remoteAddr string, localAddrs []net.Addr) bool {
|
|
host, _, err := net.SplitHostPort(strings.TrimSpace(remoteAddr))
|
|
if err != nil {
|
|
host = strings.TrimSpace(remoteAddr)
|
|
}
|
|
ip := net.ParseIP(host)
|
|
if ip == nil {
|
|
return false
|
|
}
|
|
if ip.IsLoopback() {
|
|
return true
|
|
}
|
|
for _, addr := range localAddrs {
|
|
if addr == nil {
|
|
continue
|
|
}
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
if v.IP != nil && v.IP.Equal(ip) {
|
|
return true
|
|
}
|
|
case *net.IPAddr:
|
|
if v.IP != nil && v.IP.Equal(ip) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|