Documentation ¶
Overview ¶
Package adapters defines API adapters for each supported site.
Index ¶
- Variables
- func AdapterConf(a Adapter) (merged common.AdapterConf)
- func BuildURL(a Adapter, serial string) (url string)
- func Operator(a Adapter, serial string, info JSONObject) (bureauCode string, err error)
- func Register(a Adapter)
- func SessionID(a Adapter) string
- type Adapter
- type Beijing
- func (Beijing) AlwaysOn() bool
- func (Beijing) Code() string
- func (a Beijing) Info(qrCode string) (info JSONObject, err error)
- func (Beijing) Name() string
- func (Beijing) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Beijing) URL() (pattern string, mockValue interface{})
- func (Beijing) UnitNo(_ string, info JSONObject) (unitNo string, err error)
- type Chengdu
- func (Chengdu) AlwaysOn() bool
- func (Chengdu) Code() string
- func (a Chengdu) Info(serial string) (info JSONObject, err error)
- func (Chengdu) InfoDecrypt(src io.Reader, dest interface{}) (err error)
- func (Chengdu) Name() string
- func (Chengdu) SerialEncrypt(api, serial string) (ret url.Values, err error)
- func (Chengdu) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Chengdu) URL() (pattern string, mockValue interface{})
- func (Chengdu) UnitNo(qrTuple string, info JSONObject) (unitNo string, err error)
- type Guangzhou
- func (Guangzhou) AlwaysOn() bool
- func (Guangzhou) Code() string
- func (a Guangzhou) Info(serial string) (info JSONObject, err error)
- func (Guangzhou) Name() string
- func (a Guangzhou) RoundTrip(req *http.Request) (*http.Response, error)
- func (Guangzhou) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Guangzhou) URL() (pattern string, mockValue interface{})
- func (Guangzhou) UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
- type Harbin
- func (Harbin) AlwaysOn() bool
- func (Harbin) Code() string
- func (a Harbin) Info(serial string) (info JSONObject, err error)
- func (Harbin) Name() string
- func (a Harbin) RoundTrip(req *http.Request) (*http.Response, error)
- func (a Harbin) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Harbin) URL() (pattern string, mockValue interface{})
- func (a Harbin) UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
- type JD12306
- func (JD12306) AlwaysOn() bool
- func (JD12306) Code() string
- func (a *JD12306) Info(serial string) (info JSONObject, err error)
- func (JD12306) Name() string
- func (a *JD12306) OAuth(serial string) (resp *http.Response, err error)
- func (JD12306) Operator(serialNo string, _ JSONObject) (bureauCode string, err error)
- func (a *JD12306) RefreshToken() (resp *http.Response, err error)
- func (a *JD12306) RoundTrip(req *http.Request) (*http.Response, error)
- func (JD12306) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (JD12306) URL() (pattern string, mockValue interface{})
- func (JD12306) UnitNo(_ string, info JSONObject) (unitNo string, err error)
- type JSONObject
- type Jinan
- func (Jinan) AlwaysOn() bool
- func (Jinan) Code() string
- func (a Jinan) EncryptedQuery(api string, params interface{}) (info JSONObject, err error)
- func (a Jinan) Info(serial string) (info JSONObject, err error)
- func (Jinan) InfoDecrypt(src string, dest interface{}) (err error)
- func (Jinan) InfoEncrypt(src interface{}) string
- func (Jinan) Name() string
- func (a Jinan) RoundTrip(req *http.Request) (*http.Response, error)
- func (Jinan) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Jinan) URL() (pattern string, mockValue interface{})
- func (Jinan) UnitNo(_ string, info JSONObject) (unitNo string, err error)
- type JinanQuery
- type LTDD
- func (LTDD) AlwaysOn() bool
- func (LTDD) Code() string
- func (a LTDD) Info(serial string) (info JSONObject, err error)
- func (LTDD) Name() string
- func (LTDD) Operator(_ string, info JSONObject) (bureauCode string, err error)
- func (a LTDD) RoundTrip(req *http.Request) (*http.Response, error)
- func (LTDD) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (LTDD) URL() (pattern string, mockValue interface{})
- func (LTDD) UnitNo(_ string, info JSONObject) (unitNo string, err error)
- type Mobile12306
- func (Mobile12306) AlwaysOn() bool
- func (Mobile12306) Code() string
- func (a *Mobile12306) Info(serial string) (info JSONObject, err error)
- func (Mobile12306) Name() string
- func (Mobile12306) Operator(serialNo string, info JSONObject) (bureauCode string, err error)
- func (Mobile12306) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Mobile12306) URL() (pattern string, mockValue interface{})
- func (Mobile12306) UnitNo(serialNo string, info JSONObject) (unitNo string, err error)
- type Shanghai
- func (Shanghai) AlwaysOn() bool
- func (Shanghai) Code() string
- func (a Shanghai) Info(serial string) (info JSONObject, err error)
- func (Shanghai) InfoDecrypt(src []byte, dest interface{}) (err error)
- func (Shanghai) Name() string
- func (Shanghai) Operator(_ string, _ JSONObject) (bureauCode string, err error)
- func (a Shanghai) RoundTrip(req *http.Request) (*http.Response, error)
- func (a Shanghai) SerialEncrypt(serial string) []byte
- func (Shanghai) Signature(message interface{}) string
- func (Shanghai) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (Shanghai) URL() (pattern string, mockValue interface{})
- func (Shanghai) UnitNo(_ string, info JSONObject) (unitNo string, err error)
- type ShanghaiLegacy
- func (ShanghaiLegacy) AlwaysOn() bool
- func (ShanghaiLegacy) Code() string
- func (a ShanghaiLegacy) Info(serial string) (info JSONObject, err error)
- func (ShanghaiLegacy) Name() string
- func (ShanghaiLegacy) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
- func (ShanghaiLegacy) URL() (pattern string, mockValue interface{})
- func (ShanghaiLegacy) UnitNo(_ string, info JSONObject) (unitNo string, err error)
- type TrainSchedule
- type UnionAdapter
Examples ¶
- AdapterConf
- Beijing.TrainNo
- Beijing.UnitNo
- BuildURL
- Chengdu.TrainNo
- Chengdu.UnitNo
- Guangzhou.TrainNo
- Guangzhou.UnitNo
- Harbin.TrainNo
- Harbin.UnitNo
- JD12306.TrainNo
- JD12306.UnitNo
- Jinan.InfoDecrypt
- Jinan.InfoEncrypt
- Jinan.TrainNo
- Jinan.UnitNo
- JinanQuery.Sign
- LTDD.TrainNo
- LTDD.UnitNo
- Mobile12306.TrainNo
- Mobile12306.UnitNo
- ParseURL
- SessionID
- Shanghai.SerialEncrypt
- Shanghai.Signature
- Shanghai.TrainNo
- Shanghai.UnitNo
- ShanghaiLegacy.TrainNo
- ShanghaiLegacy.UnitNo
Constants ¶
This section is empty.
Variables ¶
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 ¶
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 SessionID ¶
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 ParseURL ¶
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) 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
type Chengdu ¶
type Chengdu struct{}
func (Chengdu) InfoDecrypt ¶
InfoDecrypt decrypts the base64 encoded cipher text with DES-ECB, and unmarshals the plain text result into the given structure.
func (Chengdu) SerialEncrypt ¶
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) 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) TrainNo ¶
func (Guangzhou) TrainNo(info JSONObject) (trains []TrainSchedule, err error)
Example ¶
printTrainNo( adapters.Guangzhou{}, "guangzhou_full.json", "guangzhou_invalid.json", )
Output: false "G1363" "" true
type Harbin ¶
type Harbin struct{}
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
type JD12306 ¶
type JD12306 struct {
// contains filtered or unexported fields
}
func (*JD12306) OAuth ¶
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 ¶
RefreshToken applies for a new access token from JD pay if the cached access token has already been expired.
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) 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) EncryptedQuery ¶
func (a Jinan) EncryptedQuery(api string, params interface{}) (info JSONObject, err error)
func (Jinan) InfoDecrypt ¶
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 ¶
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) 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
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) Operator ¶
func (LTDD) Operator(_ string, info JSONObject) (bureauCode string, err 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) 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) InfoDecrypt ¶
InfoDecrypt decrypts the hexadecimal encoded cipher text with AES-ECB-128, and unmarshals the plain text result into the given structure.
func (Shanghai) Operator ¶
func (Shanghai) Operator(_ string, _ JSONObject) (bureauCode string, err error)
func (Shanghai) SerialEncrypt ¶
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 ¶
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) 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) }