commit 4196be4f670055322a10244138a204ffb9870743 Author: 諏訪子 Date: Wed Jan 21 03:36:13 2026 +0900 SVNからのミラー diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c76dd4c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,39 @@ +# 2.3.0 +* GPL → ISC + +# 2.2.0 +* GNU MakeからBSD Makeに変更 +* 社会、トレンド、地域、海外、コラム、及びライフスタイルのカテゴリページの修正 +* マウスオーバーのハイライトの追加 +* .pageList02のスタイリングの追加 + +# 2.1.0 +* 出版社ページを追加 +* 出版社ページのページネーション +* カテゴリーのページネーション +* つぶやきを見るページ(list_quote.pl)を追加 + +# 2.0.1 +* manページを修正 +* 「-h」を修正 +* エラーページを修正 + +# 2.0.0 +* PHPからGo言語に交換しました + +# 1.1.2 +* 投稿内の画像もプロクシー化 +* 投稿内の画像はレスポンシブに + +# 1.1.1 +* YouTube動画があれば、オワコンYouTubeに変更させる様に +* サムネイルがなければ、getimg()関数をす部に終了する様に +* もっと小さいバグを修正 + +# 1.1.0 +* 画像はimgproxy化 +* カテゴリーを追加 +* ホームページを追加(普通Mixiにアクセスするのは不要になった) + +# 1.0.0 +* 最初リリース 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..86218b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +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" +# Linux、Haiku、Illumos = /usr、FreeBSDとOpenBSD = /usr/local、NetBSD = /usr/pkg +PREFIX=/usr/local +MANPREFIX=${PREFIX}/share/man +# FreeBSD = /usr/local/etc、NetBSD = /usr/pkg/etc、それ以外 = /etc +CNFPREFIX?=/etc +CC=CGO_ENABLED=0 go build +# リリース。なし=デバッグ。 +RELEASE=-ldflags="-s -w" -buildvcs=false + +all: + ${CC} ${RELEASE} -o ${NAME} + +release: + env GOOS=linux GOARCH=amd64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-amd64 + env GOOS=linux GOARCH=arm64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-arm64 + env GOOS=linux GOARCH=arm ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-arm + env GOOS=linux GOARCH=riscv64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-riscv64 + env GOOS=linux GOARCH=386 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-386 + env GOOS=linux GOARCH=ppc64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-ppc64 + env GOOS=linux GOARCH=mips64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-linux-mips64 + env GOOS=openbsd GOARCH=amd64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-openbsd-amd64 + env GOOS=openbsd GOARCH=386 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-openbsd-386 + env GOOS=openbsd GOARCH=arm64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-openbsd-arm64 + env GOOS=openbsd GOARCH=arm ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-openbsd-arm + env GOOS=openbsd GOARCH=mips64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-openbsd-mips64 + env GOOS=freebsd GOARCH=amd64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-freebsd-amd64 + env GOOS=freebsd GOARCH=386 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-freebsd-386 + env GOOS=freebsd GOARCH=arm ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-freebsd-arm4 + env GOOS=freebsd GOARCH=arm64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-freebsd-arm64 + env GOOS=freebsd GOARCH=riscv64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-freebsd-riscv64 + env GOOS=netbsd GOARCH=amd64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-netbsd-amd64 + env GOOS=netbsd GOARCH=386 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-netbsd-386 + env GOOS=netbsd GOARCH=arm ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-netbsd-arm4 + env GOOS=netbsd GOARCH=arm64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-netbsd-arm64 + env GOOS=illumos GOARCH=amd64 ${CC} ${RELEASE} -o bin/${NAME}-${VERSION}-illumos-amd64 + +clean: + rm -f ${NAME} ${NAME}-${VERSION}.tar.gz + +dist: clean + mkdir -p ${NAME}-${VERSION} + cp -R LICENSE.txt Makefile README.md CHANGELOG.md\ + view static logo.jpg\ + ${NAME}.1 *.go *.json go.mod go.sum ${NAME}-${VERSION} + tar zcfv ${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION} + rm -rf ${NAME}-${VERSION} + +config: + mkdir -p ${DESTDIR}${CNFPREFIX}/spliti + cp config.json ${DESTDIR}${CNFPREFIX}/spliti + +install: all + 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 + mkdir -p ${DESTDIR}${CNFPREFIX}/${NAME} + chmod 755 ${DESTDIR}${CNFPREFIX}/${NAME} + +uninstall: + rm -f ${DESTDIOR}${PREFIX}/bin/${NAME}\ + ${DESTDIR}${MANPREFIX}/man1/${NAME}.1\ + ${DESTDIR}${CNFPREFIX}/${NAME} + +.PHONY: all release clean dist config install uninstall diff --git a/README.md b/README.md new file mode 100644 index 0000000..5967c50 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# spliti + +mixi向けプライバシーUI。 + +## 設置方法 + +### すべてのOS + +```sh +$domain="example.com" +cd /var/www/htdocs +git clone https://gitler.moe/suwako/spliti.git && cd spliti +find . -type f -name "config.json" -exec sed -i 's/mixi.076.moe/$domain/g' +``` + +### OpenBSD(オススメ) + +```sh +make +doas make install +doas make config +cd /etc +wget https://076.moe/repo/webserver/relayd/spliti.conf +mv spliti.conf relayd.conf +find . -type f -name "/etc/relayd.conf" -exec sed -i 's/DOMAIN/$domain/g' +rcctl restart relayd +``` + +### Linux + +**注意:BSD Makeをインストールして下さい。GNU Makeは未対応です。** + +```sh +bmake +doas bmake install PREFIX=/usr +doas bmake config +cp /etc/nginx/sites-enabled +wget https://076.moe/repo/webserver/nginx/spliti.conf +find . -type f -name "/etc/nginx/sites-enabled/spliti.conf" -exec sed -i 's/DOMAIN/$domain/g' +/etc/init.d/nginx restart +``` + +### FreeBSD + +```sh +make +doas make install +doas make config CNFPREFIX=/usr/local/etc +cp srv/nginx.conf /usr/local/etc/nginx/sites-enabled/spliti.conf +wget https://076.moe/repo/webserver/nginx/spliti.conf +find . -type f -name "/usr/local/etc/nginx/sites-enabled/spliti.conf" -exec sed -i 's/DOMAIN/$domain/g' +service nginx restart +``` + +### NetBSD + +```sh +make +doas make install +doas make config CNFPREFIX=/usr/pkg/etc +cp srv/nginx.conf /usr/pkg/etc/nginx/sites-enabled/spliti.conf +wget https://076.moe/repo/webserver/nginx/spliti.conf +find . -type f -name "/usr/pkg/etc/nginx/sites-enabled/spliti.conf" -exec sed -i 's/DOMAIN/$domain/g' +service nginx restart +``` + +## インスタンス一覧 + +### 一般ネット + +| ウエブサイト | [クラフレ](http://jezf25zgvxlsvuzdzm6fg2hoetmruhy4uxnolyw46tuh4jugcwc7byqd.onion/Cloudflare%E3%82%92%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%84%E7%90%86%E7%94%B1%EF%BC%88%E3%83%AA%E3%83%81%E3%83%A3%E3%83%BC%E3%83%89%E3%83%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%9E%E3%83%B3%EF%BC%89) | 注 | +| -- | -- | -- | +| [https://mixi.076.moe/](https://mixi.076.moe/) | 無 | 公式インスタンス | + +### Tor + +| オニオン | 注 | +| -- | -- | +| | | + +### I2P + +| イープサイト | 注 | +| -- | -- | +| | | diff --git a/check.go b/check.go new file mode 100644 index 0000000..0bb997f --- /dev/null +++ b/check.go @@ -0,0 +1,64 @@ +package main + +import ( + "strings" + "net/url" + "fmt" +) + +func getid(u string) (string, error) { + parse, err := url.Parse(u) + if err != nil { + return "", err + } + + params, err := url.ParseQuery(parse.RawQuery) + if err != nil { + return "", err + } + + id, ok := params["id"] + if !ok || len(id) == 0 { + return "", fmt.Errorf("IDを見つけられませんでした。") + } + + return id[0], nil +} + +/* 記事かの確認 */ +func isarticle(u string) bool { + chk := strings.Split(u, "=") + return len(chk) > 2 && + (chk[0] == "/view_news.pl?id" || chk[0] == "/view_news.pl?from" || chk[0] == "/view_news.pl?media_id" || chk[0] == "/view_news.pl?stkt") +} + +/* 部分圏かの確認 */ +func issubcat(u string) bool { + chk := strings.Split(u, "=") + return len(chk) > 1 && + (chk[0] == "/list_news_category.pl?id" || chk[0] == "/list_news_category.pl?page" || chk[0] == "/list_news_category.pl?sort" || chk[0] == "/list_news_category.pl?type" || chk[0] == "/list_news_category.pl?sub_category_id") && + strings.Contains(u, "type=bn") +} + +/* 部分かの確認 */ +func iscategory(u string) bool { + chk := strings.Split(u, "=") + return len(chk) > 1 && + (chk[0] == "/list_news_category.pl?id" || chk[0] == "/list_news_category.pl?sub_category_id" || chk[0] == "/list_news_category?from") && + !strings.Contains(u, "type=bn") +} + +/* 出版社かの確認 */ +func ispublish(u string) bool { + chk := strings.Split(u, "=") + return len(chk) > 1 && (chk[0] == "/list_news_media.pl?id" || chk[0] == "/list_news_media.pl?page") +} + +/* つぶやきかの確認 */ +func istubayaki(u string) bool { + chk := strings.Split(u, "=") + return len(chk) > 1 && + (chk[0] == "/list_quote.pl?id" || chk[0] == "/list_quote.pl?type" || chk[0] == "/list_quote.pl?sort" || chk[0] == "/list_quote.pl?news_id") && + strings.Contains(u, "type=voice") && + (strings.Contains(u, "sort=post_time") || strings.Contains(u, "sort=feedback_count")) +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..e49a173 --- /dev/null +++ b/config.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "encoding/json" + "io/ioutil" + "runtime" + "os" + "errors" + "strings" +) + +type Config struct { + configpath, webpath, domain, imgproxy, ip string +} + +func getconf () (Config, error) { + var cnf Config + + prefix := "/usr" + if runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" { + prefix += "/local" + } else if runtime.GOOS == "netbsd" { + prefix += "/pkg" + } + + cnf.configpath = "/etc/spliti/config.json" + if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" { + cnf.configpath = prefix + cnf.configpath + } + + data, err := ioutil.ReadFile(cnf.configpath) + if err != nil { + fmt.Println("config.jsonを開けられません: ", err) + return cnf, errors.New("コンフィグファイルは " + cnf.configpath + " に創作して下さい。") + } + + var payload map[string]interface{} + json.Unmarshal(data, &payload) + if payload["webpath"] == nil { + payload["webpath"] = "/var/www/htdocs/spliti" + } + if payload["domain"] == nil { + return cnf, errors.New("「domain」の値が設置していません。") + } + if payload["imgproxy"] == nil { + payload["imgproxy"] = "https://imgproxy.076.moe" + } + if payload["ip"] == nil { + payload["ip"] = "0.0.0.0" + } + if _, err := os.Stat(payload["webpath"].(string)); err != nil { + fmt.Printf("%v\n", err) + return cnf, errors.New("mkdirコマンドを使って、 " + payload["webpath"].(string)) + } + if !strings.HasPrefix(payload["domain"].(string), "http://") && !strings.HasPrefix(payload["domain"].(string), "https://") { + return cnf, errors.New("URLは「http://」又は「https://」で始める様にして下さい。") + } + cnf.webpath = payload["webpath"].(string) + cnf.domain = payload["domain"].(string) + cnf.imgproxy = payload["imgproxy"].(string) + cnf.ip = payload["ip"].(string) + payload = nil + + return cnf, nil +} diff --git a/config.json b/config.json new file mode 100644 index 0000000..35ef0c0 --- /dev/null +++ b/config.json @@ -0,0 +1,6 @@ +{ + "domain": "https://mixi.076.moe", + "webpath": "/var/www/htdocs/spliti", + "imgproxy": "https://imgproxy.076.moe", + "ip": "0.0.0.0" +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..440c2d4 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module spliti + +go 1.20 + +require ( + golang.org/x/net v0.17.0 + golang.org/x/text v0.14.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cf89ff9 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/lib.go b/lib.go new file mode 100644 index 0000000..ff98ee4 --- /dev/null +++ b/lib.go @@ -0,0 +1,44 @@ +package main + +import ( + "io" + "strings" + "bytes" + + "golang.org/x/net/html" + "golang.org/x/text/encoding/japanese" + "golang.org/x/text/transform" +) + +/* PHPであるstrip_tagsはGo言語で存在しないから、自分で作る */ +func strip_tags(data string) string { + doc, err := html.Parse(strings.NewReader(data)) + if err != nil { + panic("HTMLをパーシングに失敗。") + } + + var buf bytes.Buffer + var f func(*html.Node) + f = func(n *html.Node) { + if n.Type == html.TextNode { + buf.WriteString(n.Data) + } + for c := n.FirstChild; c != nil; c = c.NextSibling { + f(c) + } + } + f(doc) + + return buf.String() +} + +func EUCJPToUTF8(input []byte) (string, error) { + transformer := japanese.EUCJP.NewDecoder() + reader := transform.NewReader(bytes.NewReader(input), transformer) + result, err := io.ReadAll(reader) + if err != nil { + return "エンコーディングに失敗", err + } + + return string(result), nil +} diff --git a/logo.jpg b/logo.jpg new file mode 100644 index 0000000..1a7486a Binary files /dev/null and b/logo.jpg differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..492e98a --- /dev/null +++ b/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "os" + "strconv" +) + +var sofname = "spliti" +var version = "2.3.0" + +func help() { + fmt.Println("076 " + sofname + "-" + version + " - mixi向けプライバシーUI") + fmt.Println("https://mixi.076.moe/ | https://gitler.moe/suwako/spliti") + fmt.Println("") + fmt.Println("使い方:") + fmt.Println(sofname + " -v :バージョンを表示") + fmt.Println(sofname + " -s [ポート番号] :ポート番号でウエブサーバーを実行(デフォルト=9930)") + fmt.Println(sofname + " -h :ヘルプを表示") +} + +func main() { + cnf, err := getconf() + if err != nil { + fmt.Println(err) + return + } + + args := os.Args + + if len(args) == 3 && args[1] == "-s" { + if port, err := strconv.Atoi(args[2]); err != nil { + fmt.Printf("%qは数字ではありません。\n", args[2]) + return + } else { + serv(cnf, port) + return + } + } else if len(args) == 2 { + if args[1] == "-v" { + fmt.Println(sofname + "-" + version) + } else if args[1] == "-s" { + serv(cnf, 9930) + } else { + help() + return + } + } else { + help() + return + } +} diff --git a/page.go b/page.go new file mode 100644 index 0000000..11cb6a9 --- /dev/null +++ b/page.go @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" + "io" + "net/http" + "regexp" + "strings" +) + +/* ページのタイトル */ +func gettitle(str string) string { + re := regexp.MustCompile("(.*)") + matches := re.FindStringSubmatch(str) + if len(matches) > 1 { + return matches[1] + } + return "" +} + +func getimg(str string, cnf Config) string { + re := regexp.MustCompile(``) + matches := re.FindStringSubmatch(str) + if len(matches) > 1 { + return strings.Replace(matches[1], "https://", cnf.imgproxy+"/", -1) + } + return "" +} + +func getdesc(str string) string { + re := regexp.MustCompile(`
(.*?)
`) + res := re.ReplaceAllString(str, "") + return strip_tags(res) +} + +/* 記事の受取 */ +func get(url string, cnf Config) map[string]string { + // デフォルト=エラー + res := make(map[string]string) + res["title"] = "見つけられない" + res["content"] = ` +
+
+

見つけられなかった

+
+
+
+
+

ごめんね!

+
+
+ ` + res["img"] = "" + res["desc"] = "" + res["err"] = "" + + resp, err := http.Get("https://news.mixi.jp" + url) + if err != nil { + res["err"] = "URLエラー" + fmt.Println(res["err"] + ": " + err.Error()) + return res + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + bytebody, err := io.ReadAll(resp.Body) + if err != nil { + res["err"] = "内容はバイトコードとして読み込みに失敗。" + fmt.Println(res["err"]) + return res + } + + body, err := EUCJPToUTF8(bytebody) + if err != nil { + res["err"] = err.Error() + fmt.Println(res["err"]) + return res + } + + id, _ := getid(url) + + res["title"] = gettitle(body) + if isarticle(url) { + if !strings.Contains(body, "newsArticle") { + res["content"] = rmebloat(body, cnf) + } else { + res["img"] = getimg(body, cnf) + res["content"] = rmbloat(id, body, cnf) + } + } else if ispublish(url) { + res["content"] = rmpbloat(body, cnf) + } else if issubcat(url) { + if strings.Contains(body, `

存在しないカテゴリです

`) { + res["content"] = rmebloat(body, cnf) + } else { + res["content"] = rmsbloat(body, cnf) + } + } else if istubayaki(url) { + if !strings.Contains(body, "quoteList") { + res["content"] = rmebloat(body, cnf) + } else { + res["content"] = rmqbloat(body, cnf) + } + } else { + if !strings.Contains(body, "注目のニュース") { + res["content"] = rmebloat(body, cnf) + } else { + res["content"] = rmcbloat(body, cnf) + } + } + res["desc"] = getdesc(res["content"]) + } + + return res +} diff --git a/rmbloat.go b/rmbloat.go new file mode 100644 index 0000000..fbab1e3 --- /dev/null +++ b/rmbloat.go @@ -0,0 +1,203 @@ +package main + +import ( + "regexp" + "strings" +) + +/* カテゴリーだけが残るまで消す */ +func rmcbloat(body string, cnf Config) string { + var re *regexp.Regexp + + rep := []struct { + pat string + repl string + }{ + {`(?s).*?`, ""}, + {`(?s).*?`, ""}, + {`(?s)
.*?
`, ""}, + {`(?s)