Initialize Tinder API Wrapper with server configuration and Docker setup
This commit is contained in:
94
README.md
Normal file
94
README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Tinder API Wrapper
|
||||
|
||||
A Go wrapper for the Tinder API that provides easy access to basic Tinder functionality such as liking/passing users and getting recommendations.
|
||||
|
||||
## Features
|
||||
|
||||
- Simple, idiomatic Go API
|
||||
- Support for Tinder's core endpoints:
|
||||
- Like a user
|
||||
- Pass on a user
|
||||
- Get user recommendations
|
||||
- Configurable API endpoint
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get tinder-api-wrapper
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
tinder "tinder-api-wrapper"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a new client with the desired API endpoint
|
||||
client, err := tinder.NewClient("https://tinder.cloonar.com")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
|
||||
// Get recommendations
|
||||
recs, err := client.GetRecommendations("en")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get recommendations: %v", err)
|
||||
}
|
||||
|
||||
// Like a user
|
||||
likeResp, err := client.LikeUser("user123")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to like user: %v", err)
|
||||
}
|
||||
fmt.Printf("Match: %v, Likes remaining: %d\n", likeResp.Match, likeResp.LikesRemaining)
|
||||
|
||||
// Pass on a user
|
||||
passResp, err := client.PassUser("user456")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to pass on user: %v", err)
|
||||
}
|
||||
fmt.Printf("Status: %s\n", passResp.Status)
|
||||
}
|
||||
```
|
||||
|
||||
See the `example` directory for more detailed usage examples.
|
||||
|
||||
## API Methods
|
||||
|
||||
### NewClient(endpoint string) (*Client, error)
|
||||
|
||||
Creates a new Tinder API client with the specified API endpoint.
|
||||
|
||||
### (c *Client) LikeUser(userID string) (*LikeResponse, error)
|
||||
|
||||
Likes a user with the given ID.
|
||||
|
||||
### (c *Client) PassUser(userID string) (*PassResponse, error)
|
||||
|
||||
Passes on (dislikes) a user with the given ID.
|
||||
|
||||
### (c *Client) GetRecommendations(locale string) (*RecsResponse, error)
|
||||
|
||||
Retrieves a list of recommended user profiles. The `locale` parameter is optional and can be an empty string.
|
||||
|
||||
## Types
|
||||
|
||||
The wrapper includes the following main types for API responses:
|
||||
|
||||
- `LikeResponse`: Response from liking a user
|
||||
- `PassResponse`: Response from passing on a user
|
||||
- `RecsResponse`: Recommendations response containing user profiles
|
||||
- `UserRecommendation`: Detailed user profile information
|
||||
- `Photo`: User photo information
|
||||
- `InstagramPhoto`: Instagram photo information
|
||||
|
||||
## License
|
||||
|
||||
This project is available under the MIT License.
|
||||
127
auth.go
Normal file
127
auth.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package tinder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// AuthRequest represents the request body for authentication
|
||||
type AuthRequest struct {
|
||||
PhoneNumber string `json:"phone_number,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
FacebookID string `json:"facebook_id,omitempty"`
|
||||
FacebookToken string `json:"facebook_token,omitempty"`
|
||||
OTP string `json:"otp,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
}
|
||||
|
||||
// AuthResponse represents the response from authentication endpoints
|
||||
type AuthResponse struct {
|
||||
Meta struct {
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
} `json:"meta"`
|
||||
Data struct {
|
||||
APIToken string `json:"api_token,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
IsNewUser bool `json:"is_new_user,omitempty"`
|
||||
SMSSent bool `json:"sms_sent,omitempty"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ProfileResponse represents the response from the profile endpoint
|
||||
type ProfileResponse struct {
|
||||
Meta struct {
|
||||
Status int `json:"status"`
|
||||
} `json:"meta"`
|
||||
Data struct {
|
||||
User struct {
|
||||
ID string `json:"_id"`
|
||||
Bio string `json:"bio"`
|
||||
Name string `json:"name"`
|
||||
} `json:"user"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// SendPhoneLogin initiates a phone login by requesting an OTP code
|
||||
func (c *Client) SendPhoneLogin(phoneNumber string) (*AuthResponse, error) {
|
||||
authReq := AuthRequest{
|
||||
PhoneNumber: phoneNumber,
|
||||
}
|
||||
return c.doAuthRequest("/v2/auth/sms/send", authReq)
|
||||
}
|
||||
|
||||
// LoginWithOTP completes the phone login using the provided OTP code
|
||||
func (c *Client) LoginWithOTP(phoneNumber, otp string) (*AuthResponse, error) {
|
||||
authReq := AuthRequest{
|
||||
PhoneNumber: phoneNumber,
|
||||
OTP: otp,
|
||||
}
|
||||
return c.doAuthRequest("/v2/auth/sms/validate", authReq)
|
||||
}
|
||||
|
||||
// LoginWithFacebook authenticates using a Facebook ID and token
|
||||
func (c *Client) LoginWithFacebook(facebookID, facebookToken string) (*AuthResponse, error) {
|
||||
authReq := AuthRequest{
|
||||
FacebookID: facebookID,
|
||||
FacebookToken: facebookToken,
|
||||
}
|
||||
return c.doAuthRequest("/v2/auth/facebook", authReq)
|
||||
}
|
||||
|
||||
// RefreshAuth refreshes the authentication token
|
||||
func (c *Client) RefreshAuth(refreshToken string) (*AuthResponse, error) {
|
||||
authReq := AuthRequest{
|
||||
RefreshToken: refreshToken,
|
||||
}
|
||||
return c.doAuthRequest("/v2/auth/login/refresh", authReq)
|
||||
}
|
||||
|
||||
// GetProfile retrieves the current user's profile
|
||||
func (c *Client) GetProfile() (*ProfileResponse, error) {
|
||||
resp, err := c.doRequest(http.MethodGet, "/v2/profile", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("API error: %s (status code: %d)", body, resp.StatusCode)
|
||||
}
|
||||
|
||||
var profileResp ProfileResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&profileResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %v", err)
|
||||
}
|
||||
|
||||
return &profileResp, nil
|
||||
}
|
||||
|
||||
// doAuthRequest performs an authentication request and handles common auth response processing
|
||||
func (c *Client) doAuthRequest(path string, authReq AuthRequest) (*AuthResponse, error) {
|
||||
resp, err := c.doRequest(http.MethodPost, path, authReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("API error: %s (status code: %d)", body, resp.StatusCode)
|
||||
}
|
||||
|
||||
var authResp AuthResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&authResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %v", err)
|
||||
}
|
||||
|
||||
// Set the auth token if it was returned
|
||||
if authResp.Data.APIToken != "" {
|
||||
c.authToken = authResp.Data.APIToken
|
||||
}
|
||||
|
||||
return &authResp, nil
|
||||
}
|
||||
143
client.go
Normal file
143
client.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package tinder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Client represents a Tinder API client
|
||||
type Client struct {
|
||||
baseURL *url.URL
|
||||
httpClient *http.Client
|
||||
authToken string
|
||||
}
|
||||
|
||||
// NewClient creates a new Tinder API client with the specified endpoint
|
||||
func NewClient(endpoint string) (*Client, error) {
|
||||
baseURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid API endpoint: %v", err)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
baseURL: baseURL,
|
||||
httpClient: &http.Client{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithAuthToken sets the authentication token for the client
|
||||
func (c *Client) WithAuthToken(token string) *Client {
|
||||
c.authToken = token
|
||||
return c
|
||||
}
|
||||
|
||||
// doRequest performs an HTTP request with proper authentication headers
|
||||
func (c *Client) doRequest(method, path string, body interface{}) (*http.Response, error) {
|
||||
endpoint := c.baseURL.ResolveReference(&url.URL{Path: path})
|
||||
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
if body != nil {
|
||||
jsonData, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request body: %v", err)
|
||||
}
|
||||
req, err = http.NewRequest(method, endpoint.String(), bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %v", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else {
|
||||
req, err = http.NewRequest(method, endpoint.String(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.authToken != "" {
|
||||
req.Header.Set("X-Auth-Token", c.authToken)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("API request failed: %v", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// LikeUser sends a like action to a specific user
|
||||
func (c *Client) LikeUser(userID string) (*LikeResponse, error) {
|
||||
resp, err := c.doRequest(http.MethodGet, fmt.Sprintf("/like/%s", userID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("API error: %s (status code: %d)", body, resp.StatusCode)
|
||||
}
|
||||
|
||||
var likeResp LikeResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&likeResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %v", err)
|
||||
}
|
||||
|
||||
return &likeResp, nil
|
||||
}
|
||||
|
||||
// PassUser sends a pass action for a specific user
|
||||
func (c *Client) PassUser(userID string) (*PassResponse, error) {
|
||||
resp, err := c.doRequest(http.MethodGet, fmt.Sprintf("/pass/%s", userID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("API error: %s (status code: %d)", body, resp.StatusCode)
|
||||
}
|
||||
|
||||
var passResp PassResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&passResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %v", err)
|
||||
}
|
||||
|
||||
return &passResp, nil
|
||||
}
|
||||
|
||||
// GetRecommendations retrieves a list of recommended user profiles
|
||||
func (c *Client) GetRecommendations(locale string) (*RecsResponse, error) {
|
||||
endpoint := c.baseURL.ResolveReference(&url.URL{Path: "/v2/recs/core"})
|
||||
|
||||
if locale != "" {
|
||||
query := endpoint.Query()
|
||||
query.Set("locale", locale)
|
||||
endpoint.RawQuery = query.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.doRequest(http.MethodGet, endpoint.Path+"?"+endpoint.RawQuery, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("API error: %s (status code: %d)", body, resp.StatusCode)
|
||||
}
|
||||
|
||||
var recsResp RecsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&recsResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %v", err)
|
||||
}
|
||||
|
||||
return &recsResp, nil
|
||||
}
|
||||
25
cmd/server/Dockerfile
Normal file
25
cmd/server/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
FROM golang:1.18-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy go mod files
|
||||
COPY go.mod ./
|
||||
COPY go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN go build -o /tinder-proxy ./cmd/server
|
||||
|
||||
# Use a smaller image for the final container
|
||||
FROM alpine:latest
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY --from=builder /tinder-proxy /tinder-proxy
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/tinder-proxy"]
|
||||
15
cmd/server/config/config.go
Normal file
15
cmd/server/config/config.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package config
|
||||
|
||||
// Config holds the server configuration
|
||||
type Config struct {
|
||||
ListenAddr string
|
||||
TargetAPI string
|
||||
}
|
||||
|
||||
// New creates a new Config with default values
|
||||
func New() *Config {
|
||||
return &Config{
|
||||
ListenAddr: ":8080",
|
||||
TargetAPI: "https://tinder.cloonar.com",
|
||||
}
|
||||
}
|
||||
139
cmd/server/handlers/auth.go
Normal file
139
cmd/server/handlers/auth.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
tinder "tinder-api-wrapper"
|
||||
)
|
||||
|
||||
// HandleSendPhoneAuth handles the phone authentication request
|
||||
func HandleSendPhoneAuth(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var authReq struct {
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
}
|
||||
if err := readJSON(r, &authReq); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.SendPhoneLogin(authReq.PhoneNumber)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleValidateOTP handles the OTP validation request
|
||||
func HandleValidateOTP(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var authReq struct {
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
OTP string `json:"otp"`
|
||||
}
|
||||
if err := readJSON(r, &authReq); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.LoginWithOTP(authReq.PhoneNumber, authReq.OTP)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleFacebookAuth handles Facebook authentication
|
||||
func HandleFacebookAuth(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var authReq struct {
|
||||
FacebookID string `json:"facebook_id"`
|
||||
FacebookToken string `json:"facebook_token"`
|
||||
}
|
||||
if err := readJSON(r, &authReq); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.LoginWithFacebook(authReq.FacebookID, authReq.FacebookToken)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleRefreshAuth handles authentication token refresh
|
||||
func HandleRefreshAuth(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var authReq struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
if err := readJSON(r, &authReq); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.RefreshAuth(authReq.RefreshToken)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGetProfile handles profile retrieval
|
||||
func HandleGetProfile(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
authToken := r.Header.Get("X-Auth-Token")
|
||||
if authToken == "" {
|
||||
http.Error(w, "Missing authentication token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
clientWithAuth := client.WithAuthToken(authToken)
|
||||
resp, err := clientWithAuth.GetProfile()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
76
cmd/server/handlers/core.go
Normal file
76
cmd/server/handlers/core.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
tinder "tinder-api-wrapper"
|
||||
)
|
||||
|
||||
// HandleLike handles the /like/{userId} endpoint
|
||||
func HandleLike(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
path := strings.TrimPrefix(r.URL.Path, "/like/")
|
||||
if path == "" {
|
||||
http.Error(w, "User ID is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.LikeUser(path)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlePass handles the /pass/{userId} endpoint
|
||||
func HandlePass(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
path := strings.TrimPrefix(r.URL.Path, "/pass/")
|
||||
if path == "" {
|
||||
http.Error(w, "User ID is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.PassUser(path)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGetRecs handles the /v2/recs/core endpoint
|
||||
func HandleGetRecs(client *tinder.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
locale := r.URL.Query().Get("locale")
|
||||
resp, err := client.GetRecommendations(locale)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, resp)
|
||||
}
|
||||
}
|
||||
37
cmd/server/handlers/utils.go
Normal file
37
cmd/server/handlers/utils.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// readJSON reads and decodes JSON from request body
|
||||
func readJSON(r *http.Request, v interface{}) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read request body: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, v); err != nil {
|
||||
return fmt.Errorf("invalid request format: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeJSON writes JSON response with proper headers
|
||||
func writeJSON(w http.ResponseWriter, v interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// LogMiddleware logs all HTTP requests
|
||||
func LogMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s %s", r.Method, r.URL.Path)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
35
cmd/server/main.go
Normal file
35
cmd/server/main.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
tinder "tinder-api-wrapper"
|
||||
"tinder-api-wrapper/cmd/server/config"
|
||||
"tinder-api-wrapper/cmd/server/router"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Initialize configuration
|
||||
cfg := config.New()
|
||||
|
||||
// Parse command line flags
|
||||
flag.StringVar(&cfg.ListenAddr, "listen", cfg.ListenAddr, "Address to listen on (e.g., :8080)")
|
||||
flag.StringVar(&cfg.TargetAPI, "target", cfg.TargetAPI, "Target Tinder API endpoint")
|
||||
flag.Parse()
|
||||
|
||||
// Create Tinder client
|
||||
client, err := tinder.NewClient(cfg.TargetAPI)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create Tinder client: %v", err)
|
||||
}
|
||||
|
||||
// Setup router
|
||||
handler := router.Setup(client)
|
||||
|
||||
// Start server
|
||||
fmt.Printf("Starting Tinder API proxy server on %s -> %s\n", cfg.ListenAddr, cfg.TargetAPI)
|
||||
log.Fatal(http.ListenAndServe(cfg.ListenAddr, handler))
|
||||
}
|
||||
28
cmd/server/router/router.go
Normal file
28
cmd/server/router/router.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
tinder "tinder-api-wrapper"
|
||||
"tinder-api-wrapper/cmd/server/handlers"
|
||||
)
|
||||
|
||||
// Setup creates and configures the HTTP router
|
||||
func Setup(client *tinder.Client) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Core endpoints
|
||||
mux.HandleFunc("/like/", handlers.HandleLike(client))
|
||||
mux.HandleFunc("/pass/", handlers.HandlePass(client))
|
||||
mux.HandleFunc("/v2/recs/core", handlers.HandleGetRecs(client))
|
||||
|
||||
// Auth endpoints
|
||||
mux.HandleFunc("/v2/auth/sms/send", handlers.HandleSendPhoneAuth(client))
|
||||
mux.HandleFunc("/v2/auth/sms/validate", handlers.HandleValidateOTP(client))
|
||||
mux.HandleFunc("/v2/auth/facebook", handlers.HandleFacebookAuth(client))
|
||||
mux.HandleFunc("/v2/auth/login/refresh", handlers.HandleRefreshAuth(client))
|
||||
mux.HandleFunc("/v2/profile", handlers.HandleGetProfile(client))
|
||||
|
||||
// Wrap with logging middleware
|
||||
return handlers.LogMiddleware(mux)
|
||||
}
|
||||
21
default.nix
Normal file
21
default.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{ lib, buildGoModule, fetchGit }:
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "tinder-api-wrapper";
|
||||
version = "0.1.0";
|
||||
|
||||
src = fetchGit {
|
||||
url = "ssh://git@your-gitea-server.com/owner/tinder-api-wrapper.git";
|
||||
ref = "main"; # Or specific branch/tag
|
||||
# rev = "specific-commit-hash"; # Optionally pin to specific commit
|
||||
};
|
||||
|
||||
vendorSha256 = null; # Will be replaced with actual hash on first build
|
||||
|
||||
meta = with lib; {
|
||||
description = "Tinder API Wrapper Service";
|
||||
homepage = "https://your-gitea-server.com/owner/tinder-api-wrapper";
|
||||
license = licenses.mit;
|
||||
maintainers = with maintainers; [ /* add maintainers */ ];
|
||||
};
|
||||
}
|
||||
472
docs/api.yaml
Normal file
472
docs/api.yaml
Normal file
@@ -0,0 +1,472 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Tinder API
|
||||
description: >
|
||||
This is an OpenAPI specification for a subset of the Tinder API endpoints.
|
||||
It covers basic functionality including authentication, user actions (like/pass),
|
||||
and recommendations.
|
||||
version: "1.0.0"
|
||||
servers:
|
||||
- url: https://tinder.cloonar.com
|
||||
paths:
|
||||
/like/{user_id}:
|
||||
get:
|
||||
operationId: likeUser
|
||||
summary: Like a user
|
||||
description: >
|
||||
Call this endpoint to "like" a user. In the Tinder app the like action is performed
|
||||
when you swipe right or tap the heart.
|
||||
parameters:
|
||||
- name: user_id
|
||||
in: path
|
||||
description: The Tinder user ID of the profile to like.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful like response.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
match:
|
||||
type: boolean
|
||||
likes_remaining:
|
||||
type: integer
|
||||
X-Padding:
|
||||
type: string
|
||||
default:
|
||||
description: Unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/pass/{user_id}:
|
||||
get:
|
||||
operationId: passUser
|
||||
summary: Pass on a user
|
||||
description: >
|
||||
Call this endpoint to “pass” (dislike) a user. In the Tinder app this action happens
|
||||
when you swipe left or tap the red X.
|
||||
parameters:
|
||||
- name: user_id
|
||||
in: path
|
||||
description: The Tinder user ID of the profile to pass.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful pass response.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
description: A status message or code.
|
||||
default:
|
||||
description: Unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/v2/recs/core:
|
||||
get:
|
||||
operationId: getRecsCore
|
||||
summary: Get Recommendations
|
||||
description: >
|
||||
Retrieves a list of recommended Tinder profiles. An optional locale query parameter can be provided.
|
||||
parameters:
|
||||
- name: locale
|
||||
in: query
|
||||
description: Locale for recommendations (e.g. "en").
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: A list of recommended profiles.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: integer
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserRecommendation'
|
||||
default:
|
||||
description: Unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/v2/auth/sms/send:
|
||||
post:
|
||||
operationId: sendPhoneLogin
|
||||
summary: Send OTP code
|
||||
description: Initiate phone authentication by requesting an OTP code
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
phone_number:
|
||||
type: string
|
||||
description: The phone number to send the OTP to
|
||||
required:
|
||||
- phone_number
|
||||
responses:
|
||||
'200':
|
||||
description: SMS sent successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/Error'
|
||||
|
||||
/v2/auth/sms/validate:
|
||||
post:
|
||||
operationId: validateOTP
|
||||
summary: Validate OTP
|
||||
description: Complete phone authentication by validating the OTP code
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
phone_number:
|
||||
type: string
|
||||
description: The phone number the OTP was sent to
|
||||
otp:
|
||||
type: string
|
||||
description: The OTP code received via SMS
|
||||
required:
|
||||
- phone_number
|
||||
- otp
|
||||
responses:
|
||||
'200':
|
||||
description: OTP validated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/Error'
|
||||
|
||||
/v2/auth/facebook:
|
||||
post:
|
||||
operationId: facebookAuth
|
||||
summary: Facebook Authentication
|
||||
description: Authenticate using Facebook credentials
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
facebook_id:
|
||||
type: string
|
||||
description: Facebook user ID
|
||||
facebook_token:
|
||||
type: string
|
||||
description: Facebook access token
|
||||
required:
|
||||
- facebook_id
|
||||
- facebook_token
|
||||
responses:
|
||||
'200':
|
||||
description: Facebook authentication successful
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/Error'
|
||||
|
||||
/v2/auth/login/refresh:
|
||||
post:
|
||||
operationId: refreshAuth
|
||||
summary: Refresh Authentication
|
||||
description: Refresh the authentication token using a refresh token
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
description: The refresh token obtained from a previous authentication
|
||||
required:
|
||||
- refresh_token
|
||||
responses:
|
||||
'200':
|
||||
description: Token refreshed successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/Error'
|
||||
|
||||
/v2/profile:
|
||||
get:
|
||||
operationId: getProfile
|
||||
summary: Get User Profile
|
||||
description: Retrieve the authenticated user's profile information
|
||||
security:
|
||||
- BearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Profile retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProfileResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/Error'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
UserRecommendation:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
_id:
|
||||
type: string
|
||||
bio:
|
||||
type: string
|
||||
birth_date:
|
||||
type: string
|
||||
format: date-time
|
||||
name:
|
||||
type: string
|
||||
photos:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Photo'
|
||||
gender:
|
||||
type: integer
|
||||
jobs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
schools:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
instagram:
|
||||
type: object
|
||||
properties:
|
||||
last_fetch_time:
|
||||
type: string
|
||||
format: date-time
|
||||
completed_initial_fetch:
|
||||
type: boolean
|
||||
photos:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/InstagramPhoto'
|
||||
media_count:
|
||||
type: integer
|
||||
profile_picture:
|
||||
type: string
|
||||
facebook:
|
||||
type: object
|
||||
properties:
|
||||
common_connections:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
connection_count:
|
||||
type: integer
|
||||
common_interests:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
spotify:
|
||||
type: object
|
||||
properties:
|
||||
spotify_connected:
|
||||
type: boolean
|
||||
spotify_theme_track:
|
||||
type: object
|
||||
description: Track info (partial schema – extend as needed)
|
||||
distance_mi:
|
||||
type: integer
|
||||
content_hash:
|
||||
type: string
|
||||
s_number:
|
||||
type: integer
|
||||
teaser:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
string:
|
||||
type: string
|
||||
teasers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
string:
|
||||
type: string
|
||||
Photo:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
crop_info:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
width_pct:
|
||||
type: number
|
||||
x_offset_pct:
|
||||
type: number
|
||||
height_pct:
|
||||
type: number
|
||||
y_offset_pct:
|
||||
type: number
|
||||
algo:
|
||||
type: object
|
||||
properties:
|
||||
width_pct:
|
||||
type: number
|
||||
x_offset_pct:
|
||||
type: number
|
||||
height_pct:
|
||||
type: number
|
||||
y_offset_pct:
|
||||
type: number
|
||||
processed_by_bullseye:
|
||||
type: boolean
|
||||
user_customized:
|
||||
type: boolean
|
||||
url:
|
||||
type: string
|
||||
processedFiles:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
height:
|
||||
type: integer
|
||||
width:
|
||||
type: integer
|
||||
fileName:
|
||||
type: string
|
||||
extension:
|
||||
type: string
|
||||
main:
|
||||
type: boolean
|
||||
InstagramPhoto:
|
||||
type: object
|
||||
properties:
|
||||
image:
|
||||
type: string
|
||||
thumbnail:
|
||||
type: string
|
||||
ts:
|
||||
type: string
|
||||
link:
|
||||
type: string
|
||||
AuthResponse:
|
||||
type: object
|
||||
properties:
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: integer
|
||||
message:
|
||||
type: string
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
api_token:
|
||||
type: string
|
||||
description: The authentication token to use for subsequent requests
|
||||
refresh_token:
|
||||
type: string
|
||||
description: Token that can be used to refresh the authentication
|
||||
is_new_user:
|
||||
type: boolean
|
||||
description: Whether this is a new user account
|
||||
sms_sent:
|
||||
type: boolean
|
||||
description: Whether an SMS was sent (for phone authentication)
|
||||
|
||||
ProfileResponse:
|
||||
type: object
|
||||
properties:
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: integer
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
_id:
|
||||
type: string
|
||||
bio:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
|
||||
responses:
|
||||
Error:
|
||||
description: Error response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-Auth-Token
|
||||
19
example-configuration.nix
Normal file
19
example-configuration.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
# Example NixOS configuration
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./nixos-module.nix
|
||||
];
|
||||
|
||||
services.tinder-api-wrapper = {
|
||||
enable = true;
|
||||
port = 8080; # default port
|
||||
# Optionally override user/group if needed
|
||||
# user = "custom-user";
|
||||
# group = "custom-group";
|
||||
};
|
||||
|
||||
# Open firewall port
|
||||
networking.firewall.allowedTCPPorts = [ 8080 ];
|
||||
}
|
||||
56
example/example.go
Normal file
56
example/example.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
tinder "tinder-api-wrapper"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a new Tinder client with the API endpoint
|
||||
client, err := tinder.NewClient("https://tinder.cloonar.com")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create Tinder client: %v", err)
|
||||
}
|
||||
|
||||
// Example: Get recommendations
|
||||
fmt.Println("Getting recommendations...")
|
||||
recs, err := client.GetRecommendations("en")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get recommendations: %v", err)
|
||||
}
|
||||
|
||||
// Print out the recommendations
|
||||
fmt.Printf("Found %d recommendations\n", len(recs.Data.Results))
|
||||
for i, rec := range recs.Data.Results {
|
||||
fmt.Printf("%d. %s, %s\n", i+1, rec.User.Name, rec.User.Bio)
|
||||
}
|
||||
|
||||
// Example: Like a user
|
||||
if len(recs.Data.Results) > 0 {
|
||||
userID := recs.Data.Results[0].User.ID
|
||||
fmt.Printf("Liking user %s...\n", userID)
|
||||
|
||||
likeResp, err := client.LikeUser(userID)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to like user: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Like response: Match=%v, LikesRemaining=%d\n",
|
||||
likeResp.Match, likeResp.LikesRemaining)
|
||||
}
|
||||
|
||||
// Example: Pass on a user
|
||||
if len(recs.Data.Results) > 1 {
|
||||
userID := recs.Data.Results[1].User.ID
|
||||
fmt.Printf("Passing on user %s...\n", userID)
|
||||
|
||||
passResp, err := client.PassUser(userID)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to pass on user: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Pass response: Status=%s\n", passResp.Status)
|
||||
}
|
||||
}
|
||||
78
nixos-module.nix
Normal file
78
nixos-module.nix
Normal file
@@ -0,0 +1,78 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.tinder-api-wrapper;
|
||||
in {
|
||||
options.services.tinder-api-wrapper = with lib; {
|
||||
enable = mkEnableOption "Tinder API wrapper service";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8080;
|
||||
description = "Port to listen on";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "tinder-api";
|
||||
description = "User account to run service.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "tinder-api";
|
||||
description = "Group account to run service.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
nixpkgs.overlays = [
|
||||
(self: super: {
|
||||
tinder-api-wrapper = self.callPackage ./default.nix {};
|
||||
})
|
||||
];
|
||||
|
||||
users.users.${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
description = "Tinder API wrapper service user";
|
||||
};
|
||||
|
||||
users.groups.${cfg.group} = {};
|
||||
|
||||
systemd.services.tinder-api-wrapper = {
|
||||
description = "Tinder API Wrapper Service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${pkgs.tinder-api-wrapper}/bin/server -port ${toString cfg.port}";
|
||||
Restart = "always";
|
||||
RestartSec = "10";
|
||||
|
||||
# Hardening
|
||||
NoNewPrivileges = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
RemoveIPC = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
91
types.go
Normal file
91
types.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package tinder
|
||||
|
||||
// LikeResponse contains the response data from a like action
|
||||
type LikeResponse struct {
|
||||
Match bool `json:"match"`
|
||||
LikesRemaining int `json:"likes_remaining"`
|
||||
XPadding string `json:"X-Padding,omitempty"`
|
||||
}
|
||||
|
||||
// PassResponse contains the response data from a pass action
|
||||
type PassResponse struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// RecsResponse contains recommendation data
|
||||
type RecsResponse struct {
|
||||
Meta struct {
|
||||
Status int `json:"status"`
|
||||
} `json:"meta"`
|
||||
Data struct {
|
||||
Results []UserRecommendation `json:"results"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// UserRecommendation represents a recommended user profile
|
||||
type UserRecommendation struct {
|
||||
Type string `json:"type"`
|
||||
DistanceMi int `json:"distance_mi"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
SNumber int `json:"s_number"`
|
||||
User struct {
|
||||
ID string `json:"_id"`
|
||||
Bio string `json:"bio"`
|
||||
BirthDate string `json:"birth_date"`
|
||||
Name string `json:"name"`
|
||||
Photos []Photo `json:"photos"`
|
||||
Gender int `json:"gender"`
|
||||
Jobs []any `json:"jobs"`
|
||||
Schools []struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"schools"`
|
||||
} `json:"user"`
|
||||
Instagram struct {
|
||||
LastFetchTime string `json:"last_fetch_time"`
|
||||
CompletedInitialFetch bool `json:"completed_initial_fetch"`
|
||||
Photos []InstagramPhoto `json:"photos"`
|
||||
MediaCount int `json:"media_count"`
|
||||
ProfilePicture string `json:"profile_picture"`
|
||||
} `json:"instagram"`
|
||||
Spotify struct {
|
||||
SpotifyConnected bool `json:"spotify_connected"`
|
||||
SpotifyThemeTrack any `json:"spotify_theme_track"`
|
||||
} `json:"spotify"`
|
||||
}
|
||||
|
||||
// Photo represents a user photo
|
||||
type Photo struct {
|
||||
ID string `json:"id"`
|
||||
URL string `json:"url"`
|
||||
FileName string `json:"fileName"`
|
||||
Extension string `json:"extension"`
|
||||
Main bool `json:"main"`
|
||||
ProcessedFiles []struct {
|
||||
URL string `json:"url"`
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width"`
|
||||
} `json:"processedFiles"`
|
||||
CropInfo struct {
|
||||
User struct {
|
||||
WidthPct float64 `json:"width_pct"`
|
||||
XOffsetPct float64 `json:"x_offset_pct"`
|
||||
HeightPct float64 `json:"height_pct"`
|
||||
YOffsetPct float64 `json:"y_offset_pct"`
|
||||
} `json:"user"`
|
||||
ProcessedByBullseye bool `json:"processed_by_bullseye"`
|
||||
UserCustomized bool `json:"user_customized"`
|
||||
} `json:"crop_info"`
|
||||
}
|
||||
|
||||
// InstagramPhoto represents a photo from Instagram
|
||||
type InstagramPhoto struct {
|
||||
Image string `json:"image"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Ts string `json:"ts"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
// Error represents an API error
|
||||
type Error struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
Reference in New Issue
Block a user