Skip to content

ahobsonsayers/twigots

Repository files navigation

twigots

Go Reference Go Report Card License - MIT

A go package to fetch ticket listings from the Twickets Live Feed.

Includes utilities to help filter the ticket listings and find the ones you want!

Powers (the similarly creatively named) twitchets, a tool to watch for event ticket listings on Twickets and notify you so you can snap them up! 🫰

Installation

go get -u github.com/ahobsonsayers/twigots

Getting an API Key

To use this tool, you will need a Twickets API key. Although Twickets doesn't provide a free API, you can easily obtain a key by following these steps:

  1. Visit the Twickets Live Feed
  2. Open your browser's Developer Tools (F12) and navigate to the Network tab
  3. Look for the GET request to https://www.twickets.live/services/catalogue and copy the api_key query parameter. You might need to refresh the page first if nothing appears in this tab.

This API key is not provided here due to liability concerns, but it appears to be a fixed, unchanging value.

Example Usage

Warning

Although this package is functional and ready for use, it is still a work in progress and is subject to change without notice - the API and usage may be modified at any time.

Use with caution and check for updates regularly.

Example can be seen in example/main.go or below:

package main

import (
	"context"
	"log"
	"log/slog"
	"time"

	"github.com/ahobsonsayers/twigots"
	"github.com/ahobsonsayers/twigots/filter"
)

func main() {
	apiKey := "my_api_key"

	// Create twickets client (using api key)
	client, err := twigots.NewClient(apiKey)
	if err != nil {
		log.Fatal(err)
	}

	// Fetch ticket listings
	listings, err := client.FetchTicketListings(
		context.Background(),
		twigots.FetchTicketListingsInput{
			// Required
			Country: twigots.CountryUnitedKingdom, // Only UK is supported at the moment
			// Optional. See all options in godoc
			CreatedBefore: time.Now(),
			CreatedAfter:  time.Now().Add(time.Duration(-5 * time.Minute)), // 5 mins ago
			MaxNumber:     100,
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Fetched %d ticket listings", len(listings))

	// Filter ticket listings just by name
	// Use the default event name similarity (0.9) to allow minor mismatches
	hamiltonListings := filter.FilterTicketListings(
		listings,
		filter.EventName("Hamilton", filter.DefaultEventNameSimilarity),
	)
	for _, listing := range hamiltonListings {
		slog.Info(
			"Found Hamilton ticket listing",
			"Event", listing.Event.Name,
			"NumTickets", listing.NumTickets,
			"Price", listing.TotalPriceInclFee().String(),
			"OriginalPrice", listing.OriginalTicketPrice().String(),
			"URL", listing.URL(),
		)
	}

	// Filter ticket listings just by several filters
	coldplayListings := filter.FilterTicketListings(
		listings,
		filter.EventName("Coldplay", 1), // Event name similarity of 1 - exact match only
		filter.EventRegion( // Only in specific regions
			twigots.RegionLondon,
			twigots.RegionSouth,
		),
		filter.NumTickets(2),    // Exactly 2 tickets in the listing
		filter.MinDiscount(0.1), // Discount of > 10%
	)
	for _, listing := range coldplayListings {
		slog.Info(
			"Found Coldplay ticket listing",
			"Event", listing.Event.Name,
			"NumTickets", listing.NumTickets,
			"Price", listing.TotalPriceInclFee().String(),
			"OriginalPrice", listing.OriginalTicketPrice().String(),
			"URL", listing.URL(),
		)
	}
}

How does the event name matching/similarity work?

Event name similarity is calculated using a modified Smith-Waterman-Gotoh algorithm. The complexity behind this algorithm does not need to be understood, but for all intents and purposes, it can be thought of as fuzzy substring matching.

If the desired event name appears within the actual event name returned by twickets (as a substring), the event similarity will be 1. Equally, if the desired event name does not appear at all, the similarity will be 0.

Setting a required similarity below, but close to 1, will allow for small inconsistencies due to misspelling etc., but can return false positives. We recommend (and default to) a value of 0.9.

False positives can also occur if your desired event name appears in the actual event name, but the event is not the one you want. This can often happen with things like tribute bands - see the example below.

Example:

Desired event: Taylor Swift
Actual event: Taylor Swift: The Eras Tour
Similarity score: 1

Example of a false positive:

Desired event: Taylor Swift
Actual event: Miss Americana: A Tribute to Taylor Swift
Similarity score: 1 <- This is a exact match, but it is probably not the event we want

For a more in depth explanation of the string matching algorithm, see this PR.

Normalization

To help with matching, both the desired and actual event names are normalized before similarity is calculated.

This is done by:

  • Converting to lower case
  • Removing all symbols/non-alphanumeric characters (except & - see below)
  • Replacing all & symbols with and
  • Removing any the prefix
  • Trimming leading and trailing whitespace and replacing all 2+ whitespace with a single space
  • Replacing accented characters with their non-accented characters
  • Spaces are added to either side of the string, to help avoid cases where the word appears inside another word e.g. grate shouldn't match ungrateful

Why the name twigots?

Because its a stupid mash up of Tickets and Go... and also why not?

Hits

About

A go package to fetch and filter event ticket listings from Twickets 🎟️

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages