diff --git a/LICENSE b/LICENSE index 2071b23..0611969 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) +Copyright (c) 2022 Yiyang Kang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e0c313d --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module git.gensokyo.cafe/kkyy/mycurrencynet + +go 1.19 + +require ( + github.com/PuerkitoBio/goquery v1.8.0 + github.com/go-resty/resty/v2 v2.7.0 +) + +require ( + github.com/andybalholm/cascadia v1.3.1 // indirect + golang.org/x/net v0.2.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d6c2e72 --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= +github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/iso/currencies.go b/iso/currencies.go new file mode 100644 index 0000000..673d586 --- /dev/null +++ b/iso/currencies.go @@ -0,0 +1,1092 @@ +// Code generated with `go generate`. DO NOT EDIT. + +package iso + +var Currencies = map[string]ISOCurrency{ + "AFN": { + Name: "Afghani", + Code: "AFN", + Number: "971", + MinorUnit: "2", + }, + "EUR": { + Name: "Euro", + Code: "EUR", + Number: "978", + MinorUnit: "2", + }, + "ALL": { + Name: "Lek", + Code: "ALL", + Number: "008", + MinorUnit: "2", + }, + "DZD": { + Name: "Algerian Dinar", + Code: "DZD", + Number: "012", + MinorUnit: "2", + }, + "USD": { + Name: "US Dollar", + Code: "USD", + Number: "840", + MinorUnit: "2", + }, + "AOA": { + Name: "Kwanza", + Code: "AOA", + Number: "973", + MinorUnit: "2", + }, + "XCD": { + Name: "East Caribbean Dollar", + Code: "XCD", + Number: "951", + MinorUnit: "2", + }, + "ARS": { + Name: "Argentine Peso", + Code: "ARS", + Number: "032", + MinorUnit: "2", + }, + "AMD": { + Name: "Armenian Dram", + Code: "AMD", + Number: "051", + MinorUnit: "2", + }, + "AWG": { + Name: "Aruban Florin", + Code: "AWG", + Number: "533", + MinorUnit: "2", + }, + "AUD": { + Name: "Australian Dollar", + Code: "AUD", + Number: "036", + MinorUnit: "2", + }, + "AZN": { + Name: "Azerbaijan Manat", + Code: "AZN", + Number: "944", + MinorUnit: "2", + }, + "BSD": { + Name: "Bahamian Dollar", + Code: "BSD", + Number: "044", + MinorUnit: "2", + }, + "BHD": { + Name: "Bahraini Dinar", + Code: "BHD", + Number: "048", + MinorUnit: "3", + }, + "BDT": { + Name: "Taka", + Code: "BDT", + Number: "050", + MinorUnit: "2", + }, + "BBD": { + Name: "Barbados Dollar", + Code: "BBD", + Number: "052", + MinorUnit: "2", + }, + "BYN": { + Name: "Belarusian Ruble", + Code: "BYN", + Number: "933", + MinorUnit: "2", + }, + "BZD": { + Name: "Belize Dollar", + Code: "BZD", + Number: "084", + MinorUnit: "2", + }, + "XOF": { + Name: "CFA Franc BCEAO", + Code: "XOF", + Number: "952", + MinorUnit: "0", + }, + "BMD": { + Name: "Bermudian Dollar", + Code: "BMD", + Number: "060", + MinorUnit: "2", + }, + "INR": { + Name: "Indian Rupee", + Code: "INR", + Number: "356", + MinorUnit: "2", + }, + "BTN": { + Name: "Ngultrum", + Code: "BTN", + Number: "064", + MinorUnit: "2", + }, + "BOB": { + Name: "Boliviano", + Code: "BOB", + Number: "068", + MinorUnit: "2", + }, + "BOV": { + Name: "Mvdol", + Code: "BOV", + Number: "984", + MinorUnit: "2", + }, + "BAM": { + Name: "Convertible Mark", + Code: "BAM", + Number: "977", + MinorUnit: "2", + }, + "BWP": { + Name: "Pula", + Code: "BWP", + Number: "072", + MinorUnit: "2", + }, + "NOK": { + Name: "Norwegian Krone", + Code: "NOK", + Number: "578", + MinorUnit: "2", + }, + "BRL": { + Name: "Brazilian Real", + Code: "BRL", + Number: "986", + MinorUnit: "2", + }, + "BND": { + Name: "Brunei Dollar", + Code: "BND", + Number: "096", + MinorUnit: "2", + }, + "BGN": { + Name: "Bulgarian Lev", + Code: "BGN", + Number: "975", + MinorUnit: "2", + }, + "BIF": { + Name: "Burundi Franc", + Code: "BIF", + Number: "108", + MinorUnit: "0", + }, + "CVE": { + Name: "Cabo Verde Escudo", + Code: "CVE", + Number: "132", + MinorUnit: "2", + }, + "KHR": { + Name: "Riel", + Code: "KHR", + Number: "116", + MinorUnit: "2", + }, + "XAF": { + Name: "CFA Franc BEAC", + Code: "XAF", + Number: "950", + MinorUnit: "0", + }, + "CAD": { + Name: "Canadian Dollar", + Code: "CAD", + Number: "124", + MinorUnit: "2", + }, + "KYD": { + Name: "Cayman Islands Dollar", + Code: "KYD", + Number: "136", + MinorUnit: "2", + }, + "CLP": { + Name: "Chilean Peso", + Code: "CLP", + Number: "152", + MinorUnit: "0", + }, + "CLF": { + Name: "Unidad de Fomento", + Code: "CLF", + Number: "990", + MinorUnit: "4", + }, + "CNY": { + Name: "Yuan Renminbi", + Code: "CNY", + Number: "156", + MinorUnit: "2", + }, + "COP": { + Name: "Colombian Peso", + Code: "COP", + Number: "170", + MinorUnit: "2", + }, + "COU": { + Name: "Unidad de Valor Real", + Code: "COU", + Number: "970", + MinorUnit: "2", + }, + "KMF": { + Name: "Comorian Franc ", + Code: "KMF", + Number: "174", + MinorUnit: "0", + }, + "CDF": { + Name: "Congolese Franc", + Code: "CDF", + Number: "976", + MinorUnit: "2", + }, + "NZD": { + Name: "New Zealand Dollar", + Code: "NZD", + Number: "554", + MinorUnit: "2", + }, + "CRC": { + Name: "Costa Rican Colon", + Code: "CRC", + Number: "188", + MinorUnit: "2", + }, + "HRK": { + Name: "Kuna", + Code: "HRK", + Number: "191", + MinorUnit: "2", + }, + "CUP": { + Name: "Cuban Peso", + Code: "CUP", + Number: "192", + MinorUnit: "2", + }, + "CUC": { + Name: "Peso Convertible", + Code: "CUC", + Number: "931", + MinorUnit: "2", + }, + "ANG": { + Name: "Netherlands Antillean Guilder", + Code: "ANG", + Number: "532", + MinorUnit: "2", + }, + "CZK": { + Name: "Czech Koruna", + Code: "CZK", + Number: "203", + MinorUnit: "2", + }, + "DKK": { + Name: "Danish Krone", + Code: "DKK", + Number: "208", + MinorUnit: "2", + }, + "DJF": { + Name: "Djibouti Franc", + Code: "DJF", + Number: "262", + MinorUnit: "0", + }, + "DOP": { + Name: "Dominican Peso", + Code: "DOP", + Number: "214", + MinorUnit: "2", + }, + "EGP": { + Name: "Egyptian Pound", + Code: "EGP", + Number: "818", + MinorUnit: "2", + }, + "SVC": { + Name: "El Salvador Colon", + Code: "SVC", + Number: "222", + MinorUnit: "2", + }, + "ERN": { + Name: "Nakfa", + Code: "ERN", + Number: "232", + MinorUnit: "2", + }, + "SZL": { + Name: "Lilangeni", + Code: "SZL", + Number: "748", + MinorUnit: "2", + }, + "ETB": { + Name: "Ethiopian Birr", + Code: "ETB", + Number: "230", + MinorUnit: "2", + }, + "FKP": { + Name: "Falkland Islands Pound", + Code: "FKP", + Number: "238", + MinorUnit: "2", + }, + "FJD": { + Name: "Fiji Dollar", + Code: "FJD", + Number: "242", + MinorUnit: "2", + }, + "XPF": { + Name: "CFP Franc", + Code: "XPF", + Number: "953", + MinorUnit: "0", + }, + "GMD": { + Name: "Dalasi", + Code: "GMD", + Number: "270", + MinorUnit: "2", + }, + "GEL": { + Name: "Lari", + Code: "GEL", + Number: "981", + MinorUnit: "2", + }, + "GHS": { + Name: "Ghana Cedi", + Code: "GHS", + Number: "936", + MinorUnit: "2", + }, + "GIP": { + Name: "Gibraltar Pound", + Code: "GIP", + Number: "292", + MinorUnit: "2", + }, + "GTQ": { + Name: "Quetzal", + Code: "GTQ", + Number: "320", + MinorUnit: "2", + }, + "GBP": { + Name: "Pound Sterling", + Code: "GBP", + Number: "826", + MinorUnit: "2", + }, + "GNF": { + Name: "Guinean Franc", + Code: "GNF", + Number: "324", + MinorUnit: "0", + }, + "GYD": { + Name: "Guyana Dollar", + Code: "GYD", + Number: "328", + MinorUnit: "2", + }, + "HTG": { + Name: "Gourde", + Code: "HTG", + Number: "332", + MinorUnit: "2", + }, + "HNL": { + Name: "Lempira", + Code: "HNL", + Number: "340", + MinorUnit: "2", + }, + "HKD": { + Name: "Hong Kong Dollar", + Code: "HKD", + Number: "344", + MinorUnit: "2", + }, + "HUF": { + Name: "Forint", + Code: "HUF", + Number: "348", + MinorUnit: "2", + }, + "ISK": { + Name: "Iceland Krona", + Code: "ISK", + Number: "352", + MinorUnit: "0", + }, + "IDR": { + Name: "Rupiah", + Code: "IDR", + Number: "360", + MinorUnit: "2", + }, + "XDR": { + Name: "SDR (Special Drawing Right)", + Code: "XDR", + Number: "960", + MinorUnit: "N.A.", + }, + "IRR": { + Name: "Iranian Rial", + Code: "IRR", + Number: "364", + MinorUnit: "2", + }, + "IQD": { + Name: "Iraqi Dinar", + Code: "IQD", + Number: "368", + MinorUnit: "3", + }, + "ILS": { + Name: "New Israeli Sheqel", + Code: "ILS", + Number: "376", + MinorUnit: "2", + }, + "JMD": { + Name: "Jamaican Dollar", + Code: "JMD", + Number: "388", + MinorUnit: "2", + }, + "JPY": { + Name: "Yen", + Code: "JPY", + Number: "392", + MinorUnit: "0", + }, + "JOD": { + Name: "Jordanian Dinar", + Code: "JOD", + Number: "400", + MinorUnit: "3", + }, + "KZT": { + Name: "Tenge", + Code: "KZT", + Number: "398", + MinorUnit: "2", + }, + "KES": { + Name: "Kenyan Shilling", + Code: "KES", + Number: "404", + MinorUnit: "2", + }, + "KPW": { + Name: "North Korean Won", + Code: "KPW", + Number: "408", + MinorUnit: "2", + }, + "KRW": { + Name: "Won", + Code: "KRW", + Number: "410", + MinorUnit: "0", + }, + "KWD": { + Name: "Kuwaiti Dinar", + Code: "KWD", + Number: "414", + MinorUnit: "3", + }, + "KGS": { + Name: "Som", + Code: "KGS", + Number: "417", + MinorUnit: "2", + }, + "LAK": { + Name: "Lao Kip", + Code: "LAK", + Number: "418", + MinorUnit: "2", + }, + "LBP": { + Name: "Lebanese Pound", + Code: "LBP", + Number: "422", + MinorUnit: "2", + }, + "LSL": { + Name: "Loti", + Code: "LSL", + Number: "426", + MinorUnit: "2", + }, + "ZAR": { + Name: "Rand", + Code: "ZAR", + Number: "710", + MinorUnit: "2", + }, + "LRD": { + Name: "Liberian Dollar", + Code: "LRD", + Number: "430", + MinorUnit: "2", + }, + "LYD": { + Name: "Libyan Dinar", + Code: "LYD", + Number: "434", + MinorUnit: "3", + }, + "CHF": { + Name: "Swiss Franc", + Code: "CHF", + Number: "756", + MinorUnit: "2", + }, + "MOP": { + Name: "Pataca", + Code: "MOP", + Number: "446", + MinorUnit: "2", + }, + "MKD": { + Name: "Denar", + Code: "MKD", + Number: "807", + MinorUnit: "2", + }, + "MGA": { + Name: "Malagasy Ariary", + Code: "MGA", + Number: "969", + MinorUnit: "2", + }, + "MWK": { + Name: "Malawi Kwacha", + Code: "MWK", + Number: "454", + MinorUnit: "2", + }, + "MYR": { + Name: "Malaysian Ringgit", + Code: "MYR", + Number: "458", + MinorUnit: "2", + }, + "MVR": { + Name: "Rufiyaa", + Code: "MVR", + Number: "462", + MinorUnit: "2", + }, + "MRU": { + Name: "Ouguiya", + Code: "MRU", + Number: "929", + MinorUnit: "2", + }, + "MUR": { + Name: "Mauritius Rupee", + Code: "MUR", + Number: "480", + MinorUnit: "2", + }, + "XUA": { + Name: "ADB Unit of Account", + Code: "XUA", + Number: "965", + MinorUnit: "N.A.", + }, + "MXN": { + Name: "Mexican Peso", + Code: "MXN", + Number: "484", + MinorUnit: "2", + }, + "MXV": { + Name: "Mexican Unidad de Inversion (UDI)", + Code: "MXV", + Number: "979", + MinorUnit: "2", + }, + "MDL": { + Name: "Moldovan Leu", + Code: "MDL", + Number: "498", + MinorUnit: "2", + }, + "MNT": { + Name: "Tugrik", + Code: "MNT", + Number: "496", + MinorUnit: "2", + }, + "MAD": { + Name: "Moroccan Dirham", + Code: "MAD", + Number: "504", + MinorUnit: "2", + }, + "MZN": { + Name: "Mozambique Metical", + Code: "MZN", + Number: "943", + MinorUnit: "2", + }, + "MMK": { + Name: "Kyat", + Code: "MMK", + Number: "104", + MinorUnit: "2", + }, + "NAD": { + Name: "Namibia Dollar", + Code: "NAD", + Number: "516", + MinorUnit: "2", + }, + "NPR": { + Name: "Nepalese Rupee", + Code: "NPR", + Number: "524", + MinorUnit: "2", + }, + "NIO": { + Name: "Cordoba Oro", + Code: "NIO", + Number: "558", + MinorUnit: "2", + }, + "NGN": { + Name: "Naira", + Code: "NGN", + Number: "566", + MinorUnit: "2", + }, + "OMR": { + Name: "Rial Omani", + Code: "OMR", + Number: "512", + MinorUnit: "3", + }, + "PKR": { + Name: "Pakistan Rupee", + Code: "PKR", + Number: "586", + MinorUnit: "2", + }, + "PAB": { + Name: "Balboa", + Code: "PAB", + Number: "590", + MinorUnit: "2", + }, + "PGK": { + Name: "Kina", + Code: "PGK", + Number: "598", + MinorUnit: "2", + }, + "PYG": { + Name: "Guarani", + Code: "PYG", + Number: "600", + MinorUnit: "0", + }, + "PEN": { + Name: "Sol", + Code: "PEN", + Number: "604", + MinorUnit: "2", + }, + "PHP": { + Name: "Philippine Peso", + Code: "PHP", + Number: "608", + MinorUnit: "2", + }, + "PLN": { + Name: "Zloty", + Code: "PLN", + Number: "985", + MinorUnit: "2", + }, + "QAR": { + Name: "Qatari Rial", + Code: "QAR", + Number: "634", + MinorUnit: "2", + }, + "RON": { + Name: "Romanian Leu", + Code: "RON", + Number: "946", + MinorUnit: "2", + }, + "RUB": { + Name: "Russian Ruble", + Code: "RUB", + Number: "643", + MinorUnit: "2", + }, + "RWF": { + Name: "Rwanda Franc", + Code: "RWF", + Number: "646", + MinorUnit: "0", + }, + "SHP": { + Name: "Saint Helena Pound", + Code: "SHP", + Number: "654", + MinorUnit: "2", + }, + "WST": { + Name: "Tala", + Code: "WST", + Number: "882", + MinorUnit: "2", + }, + "STN": { + Name: "Dobra", + Code: "STN", + Number: "930", + MinorUnit: "2", + }, + "SAR": { + Name: "Saudi Riyal", + Code: "SAR", + Number: "682", + MinorUnit: "2", + }, + "RSD": { + Name: "Serbian Dinar", + Code: "RSD", + Number: "941", + MinorUnit: "2", + }, + "SCR": { + Name: "Seychelles Rupee", + Code: "SCR", + Number: "690", + MinorUnit: "2", + }, + "SLL": { + Name: "Leone", + Code: "SLL", + Number: "694", + MinorUnit: "2", + }, + "SLE": { + Name: "Leone", + Code: "SLE", + Number: "925", + MinorUnit: "2", + }, + "SGD": { + Name: "Singapore Dollar", + Code: "SGD", + Number: "702", + MinorUnit: "2", + }, + "XSU": { + Name: "Sucre", + Code: "XSU", + Number: "994", + MinorUnit: "N.A.", + }, + "SBD": { + Name: "Solomon Islands Dollar", + Code: "SBD", + Number: "090", + MinorUnit: "2", + }, + "SOS": { + Name: "Somali Shilling", + Code: "SOS", + Number: "706", + MinorUnit: "2", + }, + "SSP": { + Name: "South Sudanese Pound", + Code: "SSP", + Number: "728", + MinorUnit: "2", + }, + "LKR": { + Name: "Sri Lanka Rupee", + Code: "LKR", + Number: "144", + MinorUnit: "2", + }, + "SDG": { + Name: "Sudanese Pound", + Code: "SDG", + Number: "938", + MinorUnit: "2", + }, + "SRD": { + Name: "Surinam Dollar", + Code: "SRD", + Number: "968", + MinorUnit: "2", + }, + "SEK": { + Name: "Swedish Krona", + Code: "SEK", + Number: "752", + MinorUnit: "2", + }, + "CHE": { + Name: "WIR Euro", + Code: "CHE", + Number: "947", + MinorUnit: "2", + }, + "CHW": { + Name: "WIR Franc", + Code: "CHW", + Number: "948", + MinorUnit: "2", + }, + "SYP": { + Name: "Syrian Pound", + Code: "SYP", + Number: "760", + MinorUnit: "2", + }, + "TWD": { + Name: "New Taiwan Dollar", + Code: "TWD", + Number: "901", + MinorUnit: "2", + }, + "TJS": { + Name: "Somoni", + Code: "TJS", + Number: "972", + MinorUnit: "2", + }, + "TZS": { + Name: "Tanzanian Shilling", + Code: "TZS", + Number: "834", + MinorUnit: "2", + }, + "THB": { + Name: "Baht", + Code: "THB", + Number: "764", + MinorUnit: "2", + }, + "TOP": { + Name: "Pa’anga", + Code: "TOP", + Number: "776", + MinorUnit: "2", + }, + "TTD": { + Name: "Trinidad and Tobago Dollar", + Code: "TTD", + Number: "780", + MinorUnit: "2", + }, + "TND": { + Name: "Tunisian Dinar", + Code: "TND", + Number: "788", + MinorUnit: "3", + }, + "TRY": { + Name: "Turkish Lira", + Code: "TRY", + Number: "949", + MinorUnit: "2", + }, + "TMT": { + Name: "Turkmenistan New Manat", + Code: "TMT", + Number: "934", + MinorUnit: "2", + }, + "UGX": { + Name: "Uganda Shilling", + Code: "UGX", + Number: "800", + MinorUnit: "0", + }, + "UAH": { + Name: "Hryvnia", + Code: "UAH", + Number: "980", + MinorUnit: "2", + }, + "AED": { + Name: "UAE Dirham", + Code: "AED", + Number: "784", + MinorUnit: "2", + }, + "USN": { + Name: "US Dollar (Next day)", + Code: "USN", + Number: "997", + MinorUnit: "2", + }, + "UYU": { + Name: "Peso Uruguayo", + Code: "UYU", + Number: "858", + MinorUnit: "2", + }, + "UYI": { + Name: "Uruguay Peso en Unidades Indexadas (UI)", + Code: "UYI", + Number: "940", + MinorUnit: "0", + }, + "UYW": { + Name: "Unidad Previsional", + Code: "UYW", + Number: "927", + MinorUnit: "4", + }, + "UZS": { + Name: "Uzbekistan Sum", + Code: "UZS", + Number: "860", + MinorUnit: "2", + }, + "VUV": { + Name: "Vatu", + Code: "VUV", + Number: "548", + MinorUnit: "0", + }, + "VES": { + Name: "Bolívar Soberano", + Code: "VES", + Number: "928", + MinorUnit: "2", + }, + "VED": { + Name: "Bolívar Soberano", + Code: "VED", + Number: "926", + MinorUnit: "2", + }, + "VND": { + Name: "Dong", + Code: "VND", + Number: "704", + MinorUnit: "0", + }, + "YER": { + Name: "Yemeni Rial", + Code: "YER", + Number: "886", + MinorUnit: "2", + }, + "ZMW": { + Name: "Zambian Kwacha", + Code: "ZMW", + Number: "967", + MinorUnit: "2", + }, + "ZWL": { + Name: "Zimbabwe Dollar", + Code: "ZWL", + Number: "932", + MinorUnit: "2", + }, + "XBA": { + Name: "Bond Markets Unit European Composite Unit (EURCO)", + Code: "XBA", + Number: "955", + MinorUnit: "N.A.", + }, + "XBB": { + Name: "Bond Markets Unit European Monetary Unit (E.M.U.-6)", + Code: "XBB", + Number: "956", + MinorUnit: "N.A.", + }, + "XBC": { + Name: "Bond Markets Unit European Unit of Account 9 (E.U.A.-9)", + Code: "XBC", + Number: "957", + MinorUnit: "N.A.", + }, + "XBD": { + Name: "Bond Markets Unit European Unit of Account 17 (E.U.A.-17)", + Code: "XBD", + Number: "958", + MinorUnit: "N.A.", + }, + "XTS": { + Name: "Codes specifically reserved for testing purposes", + Code: "XTS", + Number: "963", + MinorUnit: "N.A.", + }, + "XXX": { + Name: "The codes assigned for transactions where no currency is involved", + Code: "XXX", + Number: "999", + MinorUnit: "N.A.", + }, + "XAU": { + Name: "Gold", + Code: "XAU", + Number: "959", + MinorUnit: "N.A.", + }, + "XPD": { + Name: "Palladium", + Code: "XPD", + Number: "964", + MinorUnit: "N.A.", + }, + "XPT": { + Name: "Platinum", + Code: "XPT", + Number: "962", + MinorUnit: "N.A.", + }, + "XAG": { + Name: "Silver", + Code: "XAG", + Number: "961", + MinorUnit: "N.A.", + }, +} diff --git a/iso/currency.go b/iso/currency.go new file mode 100644 index 0000000..30ca3f9 --- /dev/null +++ b/iso/currency.go @@ -0,0 +1,10 @@ +package iso + +type ISOCurrency struct { + Name string + Code string + Number string + MinorUnit string +} + +//go:generate go run generate.go diff --git a/iso/generate.go b/iso/generate.go new file mode 100644 index 0000000..52d2b96 --- /dev/null +++ b/iso/generate.go @@ -0,0 +1,98 @@ +//go:build ignore + +package main + +import ( + "bytes" + "encoding/xml" + "fmt" + "os" + "os/exec" + "time" + + "github.com/go-resty/resty/v2" +) + +const ( + xmlFileURL = "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml" +) + +type iso4217 struct { + XMLName xml.Name `xml:"ISO_4217"` + Published string `xml:"Pblshd,attr"` + CurrencyTable struct { + XMLName xml.Name `xml:"CcyTbl"` + CurrencyEntries []iso4217CurrencyEntry `xml:"CcyNtry"` + } `xml:"CcyTbl"` +} + +type iso4217CurrencyEntry struct { + XMLName xml.Name `xml:"CcyNtry"` + CountryName string `xml:"CtryNm"` + CurrencyName string `xml:"CcyNm"` + Currency string `xml:"Ccy"` + CurrencyNumber string `xml:"CcyNbr"` + CurrencyMinorUnits string `xml:"CcyMnrUnts"` +} + +func main() { + resp, err := resty.New(). + SetTimeout(time.Second * 10). + R().Get(xmlFileURL) + + if err != nil { + panic(err) + } + if resp.StatusCode() != 200 { + panic(fmt.Sprintf("response from %q: %d %q", xmlFileURL, resp.StatusCode(), string(resp.Body()))) + } + + var parsed iso4217 + + err = xml.Unmarshal(resp.Body(), &parsed) + if err != nil { + panic(err) + } + + buf := bytes.NewBuffer(nil) + + _, _ = fmt.Fprint(buf, "// Code generated with `go generate`. DO NOT EDIT.\n\n") + _, _ = fmt.Fprint(buf, "package iso\n\n") + _, _ = fmt.Fprint(buf, "var Currencies = map[string]ISOCurrency {\n") + + sean := map[string]struct{}{} + for _, entry := range parsed.CurrencyTable.CurrencyEntries { + if entry.Currency == "" { + continue + } + if _, ok := sean[entry.Currency]; ok { + continue + } + sean[entry.Currency] = struct{}{} + + _, _ = fmt.Fprintf(buf, "\t%q: {\n", entry.Currency) + _, _ = fmt.Fprintf(buf, "\t\t%s: %q,\n", "Name", entry.CurrencyName) + _, _ = fmt.Fprintf(buf, "\t\t%s: %q,\n", "Code", entry.Currency) + _, _ = fmt.Fprintf(buf, "\t\t%s: %q,\n", "Number", entry.CurrencyNumber) + _, _ = fmt.Fprintf(buf, "\t\t%s: %q,\n", "MinorUnit", entry.CurrencyMinorUnits) + _, _ = fmt.Fprint(buf, "\t},\n") + } + + _, _ = fmt.Fprint(buf, "}\n") + + oF, err := os.OpenFile("currencies.go", os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + panic(err) + } + + fmtCmd := exec.Command("gofmt", "-s") + fmtCmd.Stdin = bytes.NewReader(buf.Bytes()) + fmtCmd.Stdout = oF + if err = fmtCmd.Start(); err != nil { + panic(err) + } + + if err = fmtCmd.Wait(); err != nil { + panic(err) + } +} diff --git a/mycurrencynet.go b/mycurrencynet.go new file mode 100644 index 0000000..d290a58 --- /dev/null +++ b/mycurrencynet.go @@ -0,0 +1,181 @@ +package mycurrencynet + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "sync" + "time" + + "github.com/PuerkitoBio/goquery" + "github.com/go-resty/resty/v2" + + "git.gensokyo.cafe/kkyy/mycurrencynet/iso" +) + +var ( + updateUrl = "https://www.mycurrency.net/=US" + + client = resty.New() +) + +type Currency struct { + Code string + Name string + Countries []string // TODO fill this + Rate float64 +} + +func (c *Currency) To(t *Currency) float64 { + if c == nil || t == nil || c.Rate == 0 { + return 0 + } + return t.Rate / c.Rate +} + +func init() { + client. + SetTimeout(5*time.Second). + SetRetryCount(1). + SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0") +} + +func fetch() (html []byte, err error) { + resp, err := client.R().Get(updateUrl) + if err != nil { + return + } + if resp.StatusCode() != 200 { + return nil, fmt.Errorf("HTTP %d %s: %q", resp.StatusCode(), resp.Status(), resp.String()) + } + + return resp.Body(), nil +} + +func parse(body []byte) (map[string]*Currency, error) { + doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body)) + if err != nil { + return nil, err + } + ret := make(map[string]*Currency) + + doc.Find("tr.country").Each(func(_ int, s *goquery.Selection) { + var ( + c Currency + isoC iso.ISOCurrency + ok bool + err error + rate string + ) + if c.Code, ok = s.Attr("data-currency-code"); !ok { + return + } + if _, exist := ret[c.Code]; exist { + return + } + + if isoC, ok = iso.Currencies[c.Code]; !ok { + return + } + c.Name = isoC.Name + + if rate, ok = s.Find(".money[data-rate]").Attr("data-rate"); !ok { + return + } + if c.Rate, err = strconv.ParseFloat(rate, 64); err != nil { + return + } + + ret[c.Code] = &c + }) + + return ret, nil +} + +func Fetch() (map[string]*Currency, error) { + html, err := fetch() + if err != nil { + return nil, err + } + rates, err := parse(html) + if err != nil { + return nil, err + } + // add base currency + rates["USD"] = &Currency{ + Code: "USD", + Name: iso.Currencies["USD"].Name, + Rate: 1, + } + return rates, nil +} + +type MyCurrencyNet struct { + rates map[string]*Currency + lastUpdate time.Time + + mu sync.RWMutex +} + +func New() *MyCurrencyNet { + return &MyCurrencyNet{} +} + +func (m *MyCurrencyNet) Get(codes ...string) ([]*Currency, error) { + if m.rates == nil { + return nil, fmt.Errorf("not initialized.") + } + + ret := make([]*Currency, len(codes)) + m.mu.RLock() + defer m.mu.RUnlock() + + var ( + errors []string + err error = nil + ) + for i, code := range codes { + rate, ok := m.rates[strings.ToUpper(code)] + if !ok { + errors = append(errors, "unknown currency code: "+code) + continue + } + var cp Currency = *rate + ret[i] = &cp + } + if len(errors) > 0 { + err = fmt.Errorf(strings.Join(errors, ", ")) + } + return ret, err +} + +func (m *MyCurrencyNet) Update() error { + currencies, err := Fetch() + if err != nil { + return err + } + + m.mu.Lock() + defer m.mu.Unlock() + m.rates = currencies + m.lastUpdate = time.Now() + + return nil +} + +func (m *MyCurrencyNet) UpdateEvery(interval time.Duration, onErr func(error), onSuccess func()) { + for { + <-time.After(interval) + + if err := m.Update(); err != nil { + if onErr != nil { + onErr(err) + } + } else { + if onSuccess != nil { + onSuccess() + } + } + } +}