commit f14261d8d17d69ed754d5f99c4fc7833c0d86076 Author: 諏訪子 Date: Tue Jan 6 20:42:18 2026 +0900 SVNからのミラー diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..352977b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# 1.1.0 (2024/06/29) +* GNU Make → BSD Make +* GPLv2 → ISC +* 「--no-」のオプションの変更 +* help → usage +* 「同駅内徒歩」表示のバグの修正 +* 「gookit/color」という従属ソフトの取消 +* 「当駅始発」がなければ、全角空白文字で使って「◯◯線」と「◯◯行」を分けて + +# 1.0.2 (2023/10/23) +* ヤフー社はHTMLをちょっと更新されたから、乗換を修正する事が必要となった + +# 1.0.1 (2023/06/23) +* -vを修正 +* 電車・バス・空路の表示 + +# 1.0.0 (2023/06/22) +* 最初リリース diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..777a97f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright © 2018-2024 by 076.moe + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..990ed24 --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +UNAME_S != uname -s + +NAME != cat main.go | grep "var sofname" | awk '{print $$4}' | sed "s/\"//g" +VERSION != cat main.go | grep "var version" | awk '{print $$4}' | sed "s/\"//g" +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +.if ${UNAME_S} == "OpenBSD" +MANPREFIX = ${PREFIX}/man +.elif ${UNAME_S} == "Linux" +PREFIX = /usr +MANPREFIX = ${PREFIX}/share/man +.endif + +CC = CGO_ENABLED=0 go build +RELEASE = -ldflags="-s -w" -buildvcs=false + +all: + ${CC} ${RELEASE} -o ${NAME} + +release: + mkdir -p release/bin + env GOOS=linux GOARCH=amd64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-linux-amd64 + env GOOS=linux GOARCH=arm64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-linux-arm64 + env GOOS=linux GOARCH=riscv64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-linux-riscv64 + env GOOS=linux GOARCH=386 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-linux-i386 + env GOOS=linux GOARCH=ppc64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-linux-ppc64 + env GOOS=linux GOARCH=mips64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-linux-mips64 + env GOOS=openbsd GOARCH=amd64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-openbsd-amd64 + env GOOS=openbsd GOARCH=386 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-openbsd-i386 + env GOOS=openbsd GOARCH=arm64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-openbsd-arm64 + env GOOS=freebsd GOARCH=amd64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-freebsd-amd64 + env GOOS=freebsd GOARCH=386 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-freebsd-i386 + env GOOS=freebsd GOARCH=arm64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-freebsd-arm64 + env GOOS=freebsd GOARCH=riscv64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-freebsd-riscv64 + env GOOS=netbsd GOARCH=amd64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-netbsd-amd64 + env GOOS=netbsd GOARCH=386 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-netbsd-i386 + env GOOS=netbsd GOARCH=arm64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-netbsd-arm64 + env GOOS=illumos GOARCH=amd64 ${CC} ${RELEASE} -o \ + release/bin/${NAME}-${VERSION}-illumos-amd64 + +clean: + rm -f ${NAME} + +dist: + mkdir -p ${NAME}-${VERSION} release/src + cp -R LICENSE.txt Makefile README.md CHANGELOG.md\ + ${NAME}.1 main.go src go.mod go.sum ${NAME}-${VERSION} + tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION} + rm -rf ${NAME}-${VERSION} + +man: + mkdir -p release/man + sed "s/VERSION/${VERSION}/g" < ${NAME}.1 > release/man/${NAME}-${VERSION}.1 + +install: + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f ${NAME} ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME} + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < ${NAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${NAME}.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/${NAME}.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${NAME}\ + ${DESTDIR}${MANPREFIX}/man1/${NAME}.1 + +.PHONY: all release clean dist man install uninstall diff --git a/README.md b/README.md new file mode 100644 index 0000000..eacab08 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# 乗換 +CLIでの路線情報 + +## インストールする方法 +```sh +cd norikae +make +doas make install +``` + +### Linuxの場合 +```sh +cd norikae +bmake +doas bmake install +``` + +![](スクリーンショット 2026-01-06 203908.png) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..19ffb4c --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module 076/norikae + +go 1.20 + +require github.com/gocolly/colly v1.2.0 + +require ( + github.com/PuerkitoBio/goquery v1.8.1 // indirect + github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/antchfx/htmlquery v1.3.0 // indirect + github.com/antchfx/xmlquery v1.3.17 // indirect + github.com/antchfx/xpath v1.2.4 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/kennygrant/sanitize v1.2.4 // indirect + github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/temoto/robotstxt v1.1.2 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/appengine v1.6.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6c39328 --- /dev/null +++ b/go.sum @@ -0,0 +1,76 @@ +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +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/antchfx/htmlquery v1.3.0 h1:5I5yNFOVI+egyia5F2s/5Do2nFWxJz41Tr3DyfKD25E= +github.com/antchfx/htmlquery v1.3.0/go.mod h1:zKPDVTMhfOmcwxheXUsx4rKJy8KEY/PU6eXr/2SebQ8= +github.com/antchfx/xmlquery v1.3.17 h1:d0qWjPp/D+vtRw7ivCwT5ApH/3CkQU8JOeo3245PpTk= +github.com/antchfx/xmlquery v1.3.17/go.mod h1:Afkq4JIeXut75taLSuI31ISJ/zeq+3jG7TunF7noreA= +github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY= +github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gocolly/colly v1.2.0 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI= +github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= +github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg= +github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..3e63c90 --- /dev/null +++ b/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "fmt" + "os" + "time" + + "076/norikae/src" +) + +var sofname = "norikae" +var version = "1.1.0" +var avalopt = "ABEfFjmnrStX" + +func usage() { + fmt.Printf("%s-%s\nusage: %s [-%s] [string]\n", sofname, version, sofname, avalopt) +} + +func main() { + var opts src.Opts + // デフォルトな値 + t := time.Now() + opts.Date = t.Format("2006-01-02") + opts.Time = t.Format("15:04") + opts.Mode = "0" + opts.Route = "0" + opts.NoAirplane = true + opts.NoShinkansen = true + opts.NoExpress = true + opts.NoExpressBus = true + opts.NoBus = true + opts.NoFairy = true + + if len(os.Args) == 1 { + usage() + return + } + + var foundf, foundt bool + + for _, v := range os.Args { + if (v == "-f") { foundf = true } + if (v == "-t") { foundt = true } + } + + if !foundf || !foundt { + usage() + return + } + + for i := 1; i < len(os.Args); i++ { + if os.Args[i] == "-f" { opts.From = os.Args[i+1] } + if os.Args[i] == "-t" { opts.To = os.Args[i+1] } + if os.Args[i] == "-n" { opts.Date = os.Args[i+1] } + if os.Args[i] == "-j" { opts.Time = os.Args[i+1] } + if os.Args[i] == "-m" { opts.Mode = os.Args[i+1] } + if os.Args[i] == "-r" { opts.Route = os.Args[i+1] } + + if os.Args[i] == "-A" { + opts.NoAirplane = false + } + if os.Args[i] == "-S" { + opts.NoShinkansen = false + } + if os.Args[i] == "-E" { + opts.NoExpress = false + } + if os.Args[i] == "-X" { + opts.NoExpressBus = false + } + if os.Args[i] == "-B" { + opts.NoBus = false + } + if os.Args[i] == "-F" { + opts.NoFairy = false + } + } + + gurl, err := src.GetUrl(opts) + if err != nil { + fmt.Println(err) + return + } + + route := src.Scrape(gurl) + src.Render(route) +} diff --git a/norikae.1 b/norikae.1 new file mode 100644 index 0000000..577609d --- /dev/null +++ b/norikae.1 @@ -0,0 +1,52 @@ +.TH 乗換 1 norikae\-VERSION +.SH ソフト名 +乗換 - CLIでの路線情報 +.SH 概要 +.B norikae +[\fI\,オプション\/\fR] [\fI\,ほげほげ\/\fR] +.SH 説明 +.PP +CLIでの路線情報 +.SH 必須のオプション +.TP +\fB\-f [駅名]\fR +出社駅、例:秋葉原、渋谷、大手町(東京) +.TP +\fB\-t [駅名]\fR +到着駅、例:秋葉原、渋谷、大手町(東京) +.SH 任意のオプション +.TP +\fB\-n [YYYY-MM-DD]\fR +年月日、ハイフン(-)で分けて、デフォルト=今 +.TP +\fB\-j [HH:MM]\fR +時間、デフォルト=今 +.TP +\fB\-m [数字]\fR +0 = 出発、1 = 指定なし、2 = 終電、3 = 始発、4 = 到着、デフォルト=0 +.TP +\fB\-r [数字]\fR +0 = 到着が早い順、1 = 料金が高い順、2 = 乗り換え回数順、デフォルト=0 +.TP +\fB\-A\fR +空路を省く +.TP +\fB\-S\fR +新幹線を省く +.TP +\fB\-E\fR +有料特急を省く +.TP +\fB\-X\fR +高速バスを省く +.TP +\fB\-B\fR +路線/連絡バスを省く +.TP +\fB\-F\fR +フェリーを省く +.SH バグ報告 +.PP +バグは下記のURLまでご報告下さい: +.br +https://gitler.moe/suwako/norikae/issues diff --git a/src/geturl.go b/src/geturl.go new file mode 100755 index 0000000..c4cdf0b --- /dev/null +++ b/src/geturl.go @@ -0,0 +1,46 @@ +package src + +import ( + "strings" + "net/url" +) + +func b2s (val bool) string { + if val { return "1" } + return "0" +} + +func GetUrl (f Opts) (string, error) { + date := strings.Split(f.Date, "-") + year := date[0] + month := date[1] + day := date[2] + hour := strings.Split(f.Time, ":")[0] + minute := strings.Split(f.Time, ":")[1] + m1 := string(minute[0]) + m2 := string(minute[1]) + + curl, err := url.Parse( + "https://transit.yahoo.co.jp/search/result" + + "?from=" + url.QueryEscape(f.From) + + "&to=" + url.QueryEscape(f.To) + + "&y=" + year + + "&m=" + month + + "&d=" + day + + "&hh=" + hour + + "&m1=" + m1 + + "&m2=" + m2 + + "&type=" + f.Mode + + "&ticket=ic&expkind=1&userpass=1&ws=" + f.Route + + "&al=" + b2s(f.NoAirplane) + + "&shin=" + b2s(f.NoShinkansen) + + "&ex=" + b2s(f.NoExpress) + + "&hb=" + b2s(f.NoExpressBus) + + "&lb=" + b2s(f.NoBus) + + "&sr=" + b2s(f.NoFairy)) + if err != nil { + return "", nil + } + + return curl.String(), nil +} diff --git a/src/render.go b/src/render.go new file mode 100755 index 0000000..023085b --- /dev/null +++ b/src/render.go @@ -0,0 +1,65 @@ +package src + +import ( + "fmt" +) + +var r, g, b uint8 +var col string + +func getFares(v Station, k int) { + for i, fare := range v.Fares { + if k != i { + continue + } + + col = fare.Color + fmt.Sscanf(col, "%2x%2x%2x", &r, &g, &b) + text := fare.Train + + if fare.Platform != "" { + text += "\n" + fare.Platform + } + + c := fmt.Sprintf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", r, g, b, text) + fmt.Println(c) + } +} + +func Render (route []Route) { + col = "fcfcfc" + + fmt.Sscanf("ff7e56", "%2x%2x%2x", &r, &g, &b) + b1 := fmt.Sprintf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", r, g, b, "早") + + fmt.Sscanf("60bddb", "%2x%2x%2x", &r, &g, &b) + b2 := fmt.Sprintf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", r, g, b, "楽") + + fmt.Sscanf("fab60a", "%2x%2x%2x", &r, &g, &b) + b3 := fmt.Sprintf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", r, g, b, "安") + + fmt.Sscanf(col, "%2x%2x%2x", &r, &g, &b) + + for key, value := range route { + fmt.Printf("\x1b[1;35m# ルート%d\x1b[0m\n", key+1) + + badges := "" + for _, badge := range value.Badges { + if badge == 1 { badges += "〈" + b1 + "〉" } + if badge == 2 { badges += "〈" + b2 + "〉" } + if badge == 3 { badges += "〈" + b3 + "〉" } + } + + fmt.Println( + value.Time + " (" + value.Duration + "), " + + value.Fare + ", 乗換数:" + value.TransitCunt + " " + badges, + ) + + for k, v := range value.Stations { + fmt.Println(v.Time + " " + v.Name) + getFares(v, k) + } + + fmt.Println("") + } +} diff --git a/src/routedetail.go b/src/routedetail.go new file mode 100755 index 0000000..e7d01be --- /dev/null +++ b/src/routedetail.go @@ -0,0 +1,104 @@ +package src + +import ( + "strings" + "regexp" + + "github.com/gocolly/colly" +) + +func getSummary(e *colly.HTMLElement) Route { + r := Route{} + if e.Attr("class") == "icnPriTime" { r.Badges = append(r.Badges, 1) } + if e.Attr("class") == "icnPriFare" { r.Badges = append(r.Badges, 2) } + if e.Attr("class") == "icnPriTrans" { r.Badges = append(r.Badges, 3) } + + return r +} + +func handleFare(el *colly.HTMLElement, f Fare, s Stop) Fare { + fixTrain := strings.ReplaceAll( + el.ChildText("li.transport div"), "[train]", "【電車】", + ) + fixBus := strings.ReplaceAll(fixTrain, "[bus]", "【バス】") + fixAir := strings.ReplaceAll(fixBus, "[air]", "【空路】") + fixEki := strings.ReplaceAll(fixAir, "当駅始発", "【当駅始発】") + pattern := regexp.MustCompile(`(^.*線)(.*[行|方面]$)`) + + fixeki := pattern.ReplaceAllString(fixEki, "$1 $2") + + f.Train = fixeki + f.Platform = el.ChildText("li.platform") + f.Color = strings.ReplaceAll(el.ChildAttr("span", "style"), "border-color:#", "") + el.ForEach("li.stop ul", func (js int, els *colly.HTMLElement) { + s.Time = els.ChildText("li dl dt") + s.Name = strings.ReplaceAll(els.ChildText("li dl dd"), "○", "") + f.Stops = append(f.Stops, s) + }) + + return f +} + +func handleWalk(el *colly.HTMLElement, f Fare) Fare { + f.Train = strings.ReplaceAll( + el.ChildText("li.transport"), "[line][walk]", "", + ) + f.Platform = "" + f.Color = "a8a8a8" + + return f +} + +func getRouteDetail(e *colly.HTMLElement) Route { + r := Route{} + onDivs := "div.routeSummary div ul.priority li span" + e.ForEach(onDivs, func(i int, el *colly.HTMLElement) { + summary := getSummary(el) + r.Badges = append(r.Badges, summary.Badges...) + }) + + base := e.ChildText("ul.summary li.time span") + time := strings.ReplaceAll(base, e.ChildText("ul.summary li.time span.small"), "") + time2 := strings.Split(time, "着") + r.Time = time2[0] + "着" + durabase := e.ChildText("ul.summary li.time") + durasi := strings.Index(durabase, "着") + len("着") + duraei := strings.Index(durabase[durasi:], "分") + len("分") + durasi + + r.Duration = durabase[durasi:duraei] + r.TransitCunt = strings.ReplaceAll( + e.ChildText("ul.summary li.transfer"), "乗換:", "", + ) + r.Fare = strings.ReplaceAll( + e.ChildText("ul.summary li.fare"), "[priic]IC優先:", "", + ) + + onDivs = "div.routeDetail div.station" + e.ForEach(onDivs, func (j int, el *colly.HTMLElement) { + station := Station{} + station.Time = el.ChildText("ul.time li") + if el.ChildText("p.icon span") == "[dep]" { station.Time += "発" } + if el.ChildText("p.icon span") == "[arr]" { station.Time += "着" } + station.Name = el.ChildText("dl dt a") + + fares := []Fare{} + onDivs = "div.routeDetail div.fareSection div.access" + e.ForEach(onDivs, func (jf int, elf *colly.HTMLElement) { + fare := Fare{} + fare = handleFare(elf, fare, Stop{}) + fares = append(fares, fare) + }) + + onDivs = "div.routeDetail div.walk ul.info" + e.ForEach(onDivs, func (jw int, elw *colly.HTMLElement) { + fare := Fare{} + fare = handleWalk(elw, fare) + fares = append(fares, fare) + }) + + station.Fares = fares + r.Stations = append(r.Stations, station) + }) + + return r +} diff --git a/src/scrape.go b/src/scrape.go new file mode 100755 index 0000000..c284b73 --- /dev/null +++ b/src/scrape.go @@ -0,0 +1,64 @@ +package src + +import ( + "log" + "net/http" + "time" + "net" + "fmt" + + "github.com/gocolly/colly" +) + +func Scrape (gurl string) []Route { + ua := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + + "AppleWebKit/537.36 (KHTML, like Gecko) " + + "Chrome/110.0.0.0 Safari/537.36" + + sc := colly.NewCollector( + colly.AllowURLRevisit(), + colly.Async(true), + ) + + sc.WithTransport(&http.Transport { + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }) + + sc.OnRequest(func(r *colly.Request) { + r.Headers.Set("User-Agent", ua) + r.Headers.Set( + "Accept", + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + ) + r.Headers.Set("Accept-Language", "en-US,en;q=0.5") + }) + + sc.OnError(func(_ *colly.Response, err error) { + log.Fatal("エラー:", err) + }) + + var routeArr []Route + + for i := 1; i <= 3; i++ { + route := fmt.Sprintf("div#route%02d", i) + sc.OnHTML("div.elmRouteDetail " + route, func (e *colly.HTMLElement) { + Routes := getRouteDetail(e) + routeArr = append(routeArr, Routes) + }) + } + + sc.Visit(gurl) + sc.Wait() + + return routeArr +} diff --git a/src/structs.go b/src/structs.go new file mode 100755 index 0000000..8f01677 --- /dev/null +++ b/src/structs.go @@ -0,0 +1,24 @@ +package src + +type ( + Opts struct { + From, To, Date, Time, Mode, Route string + NoAirplane, NoShinkansen, NoExpress, NoExpressBus, NoBus, NoFairy bool + } + Route struct { + Time, Duration, TransitCunt, Fare string + Badges []int + Stations []Station + } + Station struct { + Time, Name string + Fares []Fare + } + Fare struct { + Train, Color, Platform string + Stops []Stop + } + Stop struct { + Time, Name string + } +) diff --git a/スクリーンショット 2026-01-06 203908.png b/スクリーンショット 2026-01-06 203908.png new file mode 100755 index 0000000..4a9be0c Binary files /dev/null and b/スクリーンショット 2026-01-06 203908.png differ