adapters

package
v0.0.0-...-43f7920 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2023 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package adapters defines API adapters for each supported site.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	Adapters = make(map[string]Adapter)
)

Functions

func AdapterConf

func AdapterConf(a Adapter) (merged common.AdapterConf)

AdapterConf merges the global request conf with adapter-specific overrides.

Example
package main

import (
	"fmt"

	"github.com/arnie97/emu-log/adapters"
	"github.com/arnie97/emu-log/common"
)

type (
	AdapterTestDefinition struct {
		SerialNoPattern string            `toml:"pattern"`
		TestCases       []AdapterTestCase `toml:"cases"`
	}
	AdapterTestCase struct {
		SerialNo string `toml:"serial"`
		URL      string `toml:"url"`
	}
	MockAdapter struct {
		adapters.Shanghai
		code string
	}
)

func (m *MockAdapter) Code() string {
	return m.code
}

func main() {
	common.MockConf()
	conf := adapters.AdapterConf(&MockAdapter{code: "X"})
	fmt.Println(conf.Request.UserAgent, int64(conf.Request.Interval))

}
Output:

Mozilla/5.0 4002

func BuildURL

func BuildURL(a Adapter, serial string) (url string)
Example
for adapterCode, testDef := range getTests() {
	a := adapters.MustGetAdapterByCode(adapterCode)
	item := testDef.TestCases[0]
	if urlBuilt := adapters.BuildURL(a, item.SerialNo); urlBuilt != item.URL {
		fmt.Println(urlBuilt)
		fmt.Println(item.URL)
	}
}
Output:

func Operator

func Operator(a Adapter, serial string, info JSONObject) (bureauCode string, err error)

func Register

func Register(a Adapter)

func SessionID

func SessionID(a Adapter) string
Example
package main

import (
	"fmt"
	"net/http"

	"github.com/arnie97/emu-log/adapters"
	"github.com/arnie97/emu-log/common"
)

type (
	AdapterTestDefinition struct {
		SerialNoPattern string            `toml:"pattern"`
		TestCases       []AdapterTestCase `toml:"cases"`
	}
	AdapterTestCase struct {
		SerialNo string `toml:"serial"`
		URL      string `toml:"url"`
	}
	MockAdapter struct {
		adapters.Shanghai
		code string
	}
)

func (m *MockAdapter) Code() string {
	return m.code
}

func main() {
	common.MockConf()
	fmt.Println(adapters.SessionID(&MockAdapter{code: "X"}))

	req, _ := http.NewRequest(http.MethodGet, "", nil)
	for _, a := range adapters.Adapters {
		if transport, ok := a.(http.RoundTripper); ok {
			transport.RoundTrip(req)
		}
	}
}
Output:

hello-world

Types

type Adapter

type Adapter interface {
	Code() string
	Name() string

	// URL returns the URL pattern contained in the QR codes,
	// with the serial number replaced by the placeholder "%s".
	URL() (pattern string, mockValue interface{})

	// AlwaysOn means the site adapter still returns some basic
	// information even if meal ordering service is currently not
	// available. Otherwise, unallocated serial numbers cannot
	// be differentiated from serials assigned to offline units,
	// and the unknown serials have to be visited in each scan.
	AlwaysOn() bool

	Info(serialNo string) (info JSONObject, err error)
	TrainNo(info JSONObject) ([]TrainSchedule, error)
	UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
}

func MustGetAdapterByCode

func MustGetAdapterByCode(adapterCode string) (a Adapter)

func ParseURL

func ParseURL(url string) (a Adapter, serial string)
Example
for adapterCode, testDef := range getTests() {
	for _, item := range testDef.TestCases {
		a, s := adapters.ParseURL(item.URL)
		if a == nil {
			fmt.Println(item.URL, "->", "?")
			continue
		}
		if a.Code() != adapterCode || s != item.SerialNo {
			fmt.Println(item.URL, "->", a.Name(), s)
		}
	}
}

fmt.Println(adapters.ParseURL("https://moerail.ml"))
Output:

<nil>

type Beijing

type Beijing struct{}

func (Beijing) AlwaysOn

func (Beijing) AlwaysOn() bool

func (Beijing) Code

func (Beijing) Code() string

func (Beijing) Info

func (a Beijing) Info(qrCode string) (info JSONObject, err error)

func (Beijing) Name

func (Beijing) Name() string

func (Beijing) TrainNo

func (Beijing) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.Beijing{},
	"beijing_full.json",
	"beijing_invalid.json",
)
Output:


false
"G666"         "2020-07-10"

true

func (Beijing) URL

func (Beijing) URL() (pattern string, mockValue interface{})

func (Beijing) UnitNo

func (Beijing) UnitNo(_ string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	adapters.Beijing{},
	"beijing_full.json",
	"beijing_invalid.json",
)
Output:

P "CR400AF0207"  false false false
P ""              true  true false

type Chengdu

type Chengdu struct{}

func (Chengdu) AlwaysOn

func (Chengdu) AlwaysOn() bool

func (Chengdu) Code

func (Chengdu) Code() string

func (Chengdu) Info

func (a Chengdu) Info(serial string) (info JSONObject, err error)

func (Chengdu) InfoDecrypt

func (Chengdu) InfoDecrypt(src io.Reader, dest interface{}) (err error)

InfoDecrypt decrypts the base64 encoded cipher text with DES-ECB, and unmarshals the plain text result into the given structure.

func (Chengdu) Name

func (Chengdu) Name() string

func (Chengdu) SerialEncrypt

func (Chengdu) SerialEncrypt(api, serial string) (ret url.Values, err error)

SerialEncrypt converts the QR code tuple to a form, and encrypts the form values in DES-ECB cipher mode.

func (Chengdu) TrainNo

func (Chengdu) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.Chengdu{},
	"chengdu_full_1.base64",
	"chengdu_full_2.base64",
	"chengdu_invalid.base64",
)
Output:


false
"C6206"        "2021-02-07 06:30"
"C6303"        "2021-02-07 08:09"
"C6304"        "2021-02-07 11:33"
"C6313"        "2021-02-07 15:03"
"C6336"        "2021-02-07 18:47"
"D6192"        "2021-02-08 07:10"
"C6307"        "2021-02-08 08:09"
"C6308"        "2021-02-08 12:43"
"C6315"        "2021-02-08 15:52"
"C6276"        "2021-02-08 19:36"

false
"G2187"        "2021-02-05 09:36"
"G2185"        "2021-02-06 06:45"

true

func (Chengdu) URL

func (Chengdu) URL() (pattern string, mockValue interface{})

func (Chengdu) UnitNo

func (Chengdu) UnitNo(qrTuple string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	adapters.Chengdu{},
	"chengdu_full_1.base64",
	"chengdu_full_2.base64",
	"chengdu_invalid.base64",
)
Output:

W "CRH3A3089"    false false false
W "CRH@1582"     false false false
W ""             false false false

type Guangzhou

type Guangzhou struct{}

func (Guangzhou) AlwaysOn

func (Guangzhou) AlwaysOn() bool

func (Guangzhou) Code

func (Guangzhou) Code() string

func (Guangzhou) Info

func (a Guangzhou) Info(serial string) (info JSONObject, err error)

func (Guangzhou) Name

func (Guangzhou) Name() string

func (Guangzhou) RoundTrip

func (a Guangzhou) RoundTrip(req *http.Request) (*http.Response, error)

func (Guangzhou) TrainNo

func (Guangzhou) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.Guangzhou{},
	"guangzhou_full.json",
	"guangzhou_invalid.json",
)
Output:


false
"G1363"        ""

true

func (Guangzhou) URL

func (Guangzhou) URL() (pattern string, mockValue interface{})

func (Guangzhou) UnitNo

func (Guangzhou) UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	adapters.Guangzhou{},
	"guangzhou_full.json",
	"guangzhou_invalid.json",
)
Output:

Q "CR16-11876@9" false false false
Q ""              true  true false

type Harbin

type Harbin struct{}

func (Harbin) AlwaysOn

func (Harbin) AlwaysOn() bool

func (Harbin) Code

func (Harbin) Code() string

func (Harbin) Info

func (a Harbin) Info(serial string) (info JSONObject, err error)

func (Harbin) Name

func (Harbin) Name() string

func (Harbin) RoundTrip

func (a Harbin) RoundTrip(req *http.Request) (*http.Response, error)

func (Harbin) TrainNo

func (a Harbin) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.Harbin{},
	"harbin_basic.html",
	"harbin_invalid.html",
)
Output:


false
"G1206/7"      ""

true

func (Harbin) URL

func (Harbin) URL() (pattern string, mockValue interface{})

func (Harbin) UnitNo

func (a Harbin) UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(adapters.Harbin{},
	"harbin_basic.html",
	"harbin_invalid.html",
)
Output:

B "CRH380BG5@42" false false false
B "CRH380BG5@42" false  true false

type JD12306

type JD12306 struct {
	// contains filtered or unexported fields
}

func (JD12306) AlwaysOn

func (JD12306) AlwaysOn() bool

func (JD12306) Code

func (JD12306) Code() string

func (*JD12306) Info

func (a *JD12306) Info(serial string) (info JSONObject, err error)

func (JD12306) Name

func (JD12306) Name() string

func (*JD12306) OAuth

func (a *JD12306) OAuth(serial string) (resp *http.Response, err error)

OAuth obtains a new authorization code from JD pay, and start a new session on 12306 servers with it. This is required for each run, since each session is bound to an immutable unit serial number.

func (JD12306) Operator

func (JD12306) Operator(serialNo string, _ JSONObject) (bureauCode string, err error)

func (*JD12306) RefreshToken

func (a *JD12306) RefreshToken() (resp *http.Response, err error)

RefreshToken applies for a new access token from JD pay if the cached access token has already been expired.

func (*JD12306) RoundTrip

func (a *JD12306) RoundTrip(req *http.Request) (*http.Response, error)

func (JD12306) TrainNo

func (JD12306) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	&adapters.JD12306{},
	"jd12306_full.json",
	"jd12306_basic.json",
	"jd12306_invalid.json",
)
Output:


false
"D2751"        "2020-07-12"

true

true

func (JD12306) URL

func (JD12306) URL() (pattern string, mockValue interface{})

func (JD12306) UnitNo

func (JD12306) UnitNo(_ string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	&adapters.JD12306{},
	"jd12306_full.json",
	"jd12306_basic.json",
	"jd12306_invalid.json",
)
Output:

J "CRH5G5194"    false false false
Y "CRH380B3667"  false false false
V ""             false  true false

type JSONObject

type JSONObject map[string]interface{}

type Jinan

type Jinan struct{}

func (Jinan) AlwaysOn

func (Jinan) AlwaysOn() bool

func (Jinan) Code

func (Jinan) Code() string

func (Jinan) EncryptedQuery

func (a Jinan) EncryptedQuery(api string, params interface{}) (info JSONObject, err error)

func (Jinan) Info

func (a Jinan) Info(serial string) (info JSONObject, err error)

func (Jinan) InfoDecrypt

func (Jinan) InfoDecrypt(src string, dest interface{}) (err error)

InfoDecrypt decrypts the base64 encoded cipher text with AES-CBC-128, and unmarshals the plain text result into the given structure.

Example
package main

import (
	"fmt"

	"github.com/arnie97/emu-log/adapters"
)

func main() {
	var info interface{}
	adapters.Jinan{}.InfoDecrypt(
		"9RG2W94kpJJa9wxaAWL1/849sBfzHmbkmO7X3fBV1DU=", &info,
	)
	fmt.Println(info)
}
Output:

map[czNo:CRH380BL-5501]

func (Jinan) InfoEncrypt

func (Jinan) InfoEncrypt(src interface{}) string

InfoEncrypt encrypts the JSON string in AES-CBC-128 cipher mode, and return the cipher text encoded with padded base64 encoding scheme.

Example
package main

import (
	"fmt"

	"github.com/arnie97/emu-log/adapters"
)

func main() {
	fmt.Println(adapters.Jinan{}.InfoEncrypt(map[string]string{
		"seatCode": "K1001641584150",
	}))
}
Output:

7x2dNPLNuBHTpL9Bc6z2JiKabX+sFyBLS4w1L0Ulbkw=

func (Jinan) Name

func (Jinan) Name() string

func (Jinan) RoundTrip

func (a Jinan) RoundTrip(req *http.Request) (*http.Response, error)

func (Jinan) TrainNo

func (Jinan) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.Jinan{},
	"jinan_full.json",
	"jinan_basic.json",
)
Output:


false
"G297/G300"    "2020-08-10"

false

func (Jinan) URL

func (Jinan) URL() (pattern string, mockValue interface{})

func (Jinan) UnitNo

func (Jinan) UnitNo(_ string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	adapters.Jinan{},
	"jinan_full.json",
	"jinan_basic.json",
)
Output:

K "CR400AFA2115" false false false
K "CRH380BL5501" false false false

type JinanQuery

type JinanQuery struct {
	Params    string `json:"params"`
	Timestamp int64  `json:"timeStamp"`
	CGUID     string `json:"cguid"`
	Token     string `json:"token,omitempty"`
	IsSign    int    `json:"isSign"`
	Signature string `json:"sign,omitempty"`
}

func (JinanQuery) Sign

func (q JinanQuery) Sign() string

Sign serializes the message in a deterministic manner, and generates its hexadecimal encoded MD5 digest.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/arnie97/emu-log/adapters"
	"github.com/arnie97/emu-log/common"
)

func main() {
	var exampleInput adapters.JinanQuery
	json.Unmarshal(common.ReadMockFile("jinan_input.json"), &exampleInput)
	fmt.Println(exampleInput.Sign() == exampleInput.Signature)
}
Output:

true

type LTDD

type LTDD struct{}

func (LTDD) AlwaysOn

func (LTDD) AlwaysOn() bool

func (LTDD) Code

func (LTDD) Code() string

func (LTDD) Info

func (a LTDD) Info(serial string) (info JSONObject, err error)

func (LTDD) Name

func (LTDD) Name() string

func (LTDD) Operator

func (LTDD) Operator(_ string, info JSONObject) (bureauCode string, err error)

func (LTDD) RoundTrip

func (a LTDD) RoundTrip(req *http.Request) (*http.Response, error)

func (LTDD) TrainNo

func (LTDD) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.LTDD{},
	"ltdd_full.html",
	"ltdd_anonymous.html",
	"ltdd_basic.html",
	"ltdd_invalid.html",
)
Output:


false
"D2936"        ""

false
"G551"         ""

true

true

func (LTDD) URL

func (LTDD) URL() (pattern string, mockValue interface{})

func (LTDD) UnitNo

func (LTDD) UnitNo(_ string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	adapters.LTDD{},
	"ltdd_full.html",
	"ltdd_anonymous.html",
	"ltdd_basic.html",
	"ltdd_invalid.html",
)
Output:

Z "CRH2A2276"    false false false
N "CR400AF2158"  false false false
W "CRH380D1545"  false false false
  ""             false  true  true

type Mobile12306

type Mobile12306 struct{}

func (Mobile12306) AlwaysOn

func (Mobile12306) AlwaysOn() bool

func (Mobile12306) Code

func (Mobile12306) Code() string

func (*Mobile12306) Info

func (a *Mobile12306) Info(serial string) (info JSONObject, err error)

func (Mobile12306) Name

func (Mobile12306) Name() string

func (Mobile12306) Operator

func (Mobile12306) Operator(serialNo string, info JSONObject) (bureauCode string, err error)

func (Mobile12306) TrainNo

func (Mobile12306) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	&adapters.Mobile12306{},
	"mobile12306_full.json",
	"mobile12306_invalid.json",
)
Output:


false
"D3830"        "2022-07-20 18:21"

true

func (Mobile12306) URL

func (Mobile12306) URL() (pattern string, mockValue interface{})

func (Mobile12306) UnitNo

func (Mobile12306) UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(
	&adapters.Mobile12306{},
	"mobile12306_full.json",
	"mobile12306_invalid.json",
)
Output:

M "CRH2A4088"    false false false
H ""              true  true false

type Shanghai

type Shanghai struct{}

func (Shanghai) AlwaysOn

func (Shanghai) AlwaysOn() bool

func (Shanghai) Code

func (Shanghai) Code() string

func (Shanghai) Info

func (a Shanghai) Info(serial string) (info JSONObject, err error)

func (Shanghai) InfoDecrypt

func (Shanghai) InfoDecrypt(src []byte, dest interface{}) (err error)

InfoDecrypt decrypts the hexadecimal encoded cipher text with AES-ECB-128, and unmarshals the plain text result into the given structure.

func (Shanghai) Name

func (Shanghai) Name() string

func (Shanghai) Operator

func (Shanghai) Operator(_ string, _ JSONObject) (bureauCode string, err error)

func (Shanghai) RoundTrip

func (a Shanghai) RoundTrip(req *http.Request) (*http.Response, error)

func (Shanghai) SerialEncrypt

func (a Shanghai) SerialEncrypt(serial string) []byte

SerialEncrypt first wraps the serial number in JSON, then calculates the hash digest signature and put that in another JSON key, next encode the JSON string with padded base64 encoding scheme, after that encrypts the base64-encoded string with AES-ECB cipher mode, and finally wrap the result in JSON again.

Example
package main

import (
	"fmt"

	"github.com/arnie97/emu-log/adapters"
	"github.com/arnie97/emu-log/common"
)

const shanghaiTestSerial = "PQC47BDAA1B1AC46C2AD545D93B9E30BA3"

func main() {
	common.MockStaticUnixMilli(1632636012773)
	fmt.Println(string(adapters.Shanghai{}.SerialEncrypt(shanghaiTestSerial)))
}
Output:

{"data":"bwmZtecxmGBJrIVOpsB8/n66ix922um4AhzjOb5eFuZKWQdSJzA1C0BJIFo5iv9C4QyafxmIswLZWx6AhA0szfxxP52mFA7xifBIS/66xhEuEeNgJTisY69iXu9WKtHYVHZ5ywMpI7sBr1Xu7BzmL50UmcLitNYlcWg8OC7ry+wTJqlRwbHeYk8zwia54qiQIBjZeKJITcPtM7c8midgnA=="}

func (Shanghai) Signature

func (Shanghai) Signature(message interface{}) string

Signature serializes the message, and generates its hexadecimal hash digest with the MD5 - SHA1 hash chain.

Example
package main

import (
	"fmt"

	"github.com/arnie97/emu-log/adapters"
)

const shanghaiTestSerial = "PQC47BDAA1B1AC46C2AD545D93B9E30BA3"

func main() {
	fmt.Println(adapters.Shanghai{}.Signature(map[string]string{
		"pqCode": shanghaiTestSerial,
	}))
}
Output:

2f9affaf878b65cf3a80

func (Shanghai) TrainNo

func (Shanghai) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.Shanghai{},
	"shanghai_full.json",
	"shanghai_basic.json",
	"shanghai_invalid.json",
)
Output:


false
"G8"           "2021-09-27 13:27:00"

true

true

func (Shanghai) URL

func (Shanghai) URL() (pattern string, mockValue interface{})

func (Shanghai) UnitNo

func (Shanghai) UnitNo(_ string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(adapters.Shanghai{},
	"shanghai_full.json",
	"shanghai_basic.json",
	"shanghai_invalid.json",
)
Output:

H "CR400BFB5097" false false false
H ""              true  true false
H ""              true  true false

type ShanghaiLegacy

type ShanghaiLegacy struct{}

func (ShanghaiLegacy) AlwaysOn

func (ShanghaiLegacy) AlwaysOn() bool

func (ShanghaiLegacy) Code

func (ShanghaiLegacy) Code() string

func (ShanghaiLegacy) Info

func (a ShanghaiLegacy) Info(serial string) (info JSONObject, err error)

func (ShanghaiLegacy) Name

func (ShanghaiLegacy) Name() string

func (ShanghaiLegacy) TrainNo

func (ShanghaiLegacy) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example
printTrainNo(
	adapters.ShanghaiLegacy{},
	"shanghai_legacy_full.json",
	"shanghai_legacy_basic.json",
	"shanghai_legacy_invalid.json",
)
Output:


false
"D3074/D3071"  ""

false

true

func (ShanghaiLegacy) URL

func (ShanghaiLegacy) URL() (pattern string, mockValue interface{})

func (ShanghaiLegacy) UnitNo

func (ShanghaiLegacy) UnitNo(_ string, info JSONObject) (unitNo string, err error)
Example
printUnitNo(adapters.ShanghaiLegacy{},
	"shanghai_legacy_full.json",
	"shanghai_legacy_basic.json",
	"shanghai_legacy_invalid.json",
)
Output:

H "CRH2A2001"    false false false
H "CRH2C2150"    false false false
H ""              true  true false

type TrainSchedule

type TrainSchedule struct{ TrainNo, Date string }

type UnionAdapter

type UnionAdapter interface {
	Adapter
	Operator(serialNo string, info JSONObject) (bureauCode string, err error)
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL