SVNからのミラー

This commit is contained in:
2026-01-21 03:36:13 +09:00
commit 4196be4f67
25 changed files with 1204 additions and 0 deletions

39
CHANGELOG.md Normal file
View File

@@ -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
* 最初リリース

13
LICENSE.txt Normal file
View File

@@ -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.

69
Makefile Normal file
View File

@@ -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

85
README.md Normal file
View File

@@ -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
| イープサイト | 注 |
| -- | -- |
| | |

64
check.go Normal file
View File

@@ -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"))
}

66
config.go Normal file
View File

@@ -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
}

6
config.json Normal file
View File

@@ -0,0 +1,6 @@
{
"domain": "https://mixi.076.moe",
"webpath": "/var/www/htdocs/spliti",
"imgproxy": "https://imgproxy.076.moe",
"ip": "0.0.0.0"
}

8
go.mod Normal file
View File

@@ -0,0 +1,8 @@
module spliti
go 1.20
require (
golang.org/x/net v0.17.0
golang.org/x/text v0.14.0
)

4
go.sum Normal file
View File

@@ -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=

44
lib.go Normal file
View File

@@ -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
}

BIN
logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

52
main.go Normal file
View File

@@ -0,0 +1,52 @@
package main
import (
"fmt"
"os"
"strconv"
)
var sofname = "spliti"
var version = "2.3.0"
func help() {
fmt.Println(" " + 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
}
}

115
page.go Normal file
View File

@@ -0,0 +1,115 @@
package main
import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
)
/* ページのタイトル */
func gettitle(str string) string {
re := regexp.MustCompile("<title>(.*)</title>")
matches := re.FindStringSubmatch(str)
if len(matches) > 1 {
return matches[1]
}
return ""
}
func getimg(str string, cnf Config) string {
re := regexp.MustCompile(`<img class="NEWS_tempPhoto__picture" src="(.*)" alt="">`)
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(`<div class="newsArticle">(.*?)</div>`)
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"] = `
<div class="newsArticle"><div class="articleHeading02">
<div class="headingArea">
<h1>見つけられなかった</h1>
</div>
</div>
<div class="contents clearfix">
<div class="article decoratable">
<p>ごめんね!</p>
</div>
</div>
`
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, `<p class="messageAlert">存在しないカテゴリです</p>`) {
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
}

203
rmbloat.go Normal file
View File

@@ -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)<!DOCTYPE html>.*?<!--注目のニュース-->`, ""},
{`(?s)<!--/newsCategoryList-->.*?</html>`, ""},
{`(?s)<div class="COMMONDOC_header2017_mainNavHeader.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_mainNav.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_toggleNav.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_localNavArea.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_globalNav__account.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_globalNav__logo.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_globalNav__toggleNav.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_adBanner.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_globalNav.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_globalNavArea.*?</div>`, ""},
{`(?s)<div class="COMMONDOC_header2017_headerArea.*?</div>`, ""},
{`(?s)<div id="page" class="FRAME2016_page">.*?</div>`, ""},
{`(?s)<div id="subCategoryNavi.*?</div>`, ""},
{`(?s)<div role="navigation".*?</div>`, ""},
{`(?s)<div id="div-gpt-ad-.*?</div>`, ""},
{`(?s)<h3.*?</h3>`, ""},
{`(?s)<script.*?</script>`, ""},
{`(?s)<ul class="entryList0.*?</ul>`, ""},
{`(?s)<div class="adMain.*?</div>`, ""},
{`(?s)<div class="gAdComponent.*?</div>`, ""},
{`(?s)<div class="adsense0.*?</div>`, ""},
{`(?s)<div class="adsense.*?</div>`, ""},
{`(?s)<div class="pageList02.*?</div>`, ""},
{`(?s)<span class="reactionCountBalloon.*?</span>`, ""},
{`https://news-image.mixi.net`, cnf.imgproxy + `/news-image.mixi.net`},
{`https://img.mixi.net`, cnf.imgproxy + `/img.mixi.net`},
{`https://news.mixi.jp/`, cnf.domain + `/`},
{``, ""},
{`\[`, ""},
{`\]`, ""},
}
for _, r := range rep {
re = regexp.MustCompile(r.pat)
body = re.ReplaceAllString(body, r.repl)
}
body = strings.TrimSpace("<div class=\"subCategoryNavi\" class=\"LEGACY_UI2016_subCategoryNavi\">\n" + strings.TrimSpace(body)) + "\n </div>\n"
return "<div class=\"newsArticle\">\n<a class=\"totop\" href=\"/\">トップへ</a>\n" + body + "</div>"
}
/* エラーだけが残るまで消す */
func rmebloat(body string, cnf Config) string {
var re *regexp.Regexp
rep := []struct {
pat string
repl string
}{
{`(?s)<!DOCTYPE html>.*?<p class="messageAlert">`, ""},
{`(?s)</p>.*?</html>`, ""},
}
for _, r := range rep {
re = regexp.MustCompile(r.pat)
body = re.ReplaceAllString(body, r.repl)
}
body = strings.TrimSpace("<div class=\"newsArticle\">\n<a class=\"totop\" href=\"/\">トップへ</a>\n" + strings.TrimSpace(body)) + "\n </div>\n"
return body
}
/* つばやきだけが残るまで消す */
func rmqbloat(body string, cnf Config) string {
var re *regexp.Regexp
rep := []struct {
pat string
repl string
}{
{`(?s)<!DOCTYPE html>.*?<div id="bodyMainArea" class="FRAME2016_bodyMainArea" >`, ""},
{`(?s)<div class="adsenseBannerArea">.*?</html>`, ""},
{`(?s)<div class="shareButtonArea">.*?<div class="relationNewsDescription">`, `<div class="relationNewsDescription">`},
{`https://news-image.mixi.net`, cnf.imgproxy + `/news-image.mixi.net`},
{`https://img.mixi.net`, cnf.imgproxy + `/img.mixi.net`},
{`https://news.mixi.jp/`, cnf.domain + `/`},
{`(?s)<div class="sortSwitch01">.*?</div>`, ""},
// {`・ `, ""},
// {`\[`, ""},
// {`\]`, ""},
}
for _, r := range rep {
re = regexp.MustCompile(r.pat)
body = re.ReplaceAllString(body, r.repl)
}
body = strings.TrimSpace("<div class=\"newsArticle\">\n<a class=\"totop\" href=\"/\">トップへ</a>\n" + strings.TrimSpace(body)) + "\n </div>\n"
return body
}
/* 部分圏だけが残るまで消す */
func rmsbloat(body string, cnf Config) string {
var re *regexp.Regexp
rep := []struct {
pat string
repl string
}{
{`(?s)<!DOCTYPE html>.*?<!-- InstanceBeginEditable name="bodyMain" -->`, ""},
{`(?s)<div class="adsenseBannerArea">.*?</html>`, ""},
{`https://news-image.mixi.net`, cnf.imgproxy + `/news-image.mixi.net`},
{`https://img.mixi.net`, cnf.imgproxy + `/img.mixi.net`},
{`https://news.mixi.jp/`, cnf.domain + `/`},
{``, ""},
{`\[`, ""},
{`\]`, ""},
}
for _, r := range rep {
re = regexp.MustCompile(r.pat)
body = re.ReplaceAllString(body, r.repl)
}
body = strings.TrimSpace("<div class=\"newsArticle\">\n<a class=\"totop\" href=\"/\">トップへ</a>\n" + strings.TrimSpace(body)) + "\n </div>\n"
return body
}
/* 出版社だけが残るまで消す */
func rmpbloat(body string, cnf Config) string {
var re *regexp.Regexp
rep := []struct {
pat string
repl string
}{
{`(?s)<!DOCTYPE html>.*?<!-- InstanceBeginEditable name="bodyMain" -->`, ""},
{`(?s)<!-- InstanceEndEditable -->.*?</html>`, ""},
{`(?s)<div class="pageList02.*?</div>`, ""},
{`https://news-image.mixi.net`, cnf.imgproxy + `/news-image.mixi.net`},
{`https://img.mixi.net`, cnf.imgproxy + `/img.mixi.net`},
{`https://news.mixi.jp/`, cnf.domain + `/`},
{``, ""},
{`\[`, ""},
{`\]`, ""},
}
for _, r := range rep {
re = regexp.MustCompile(r.pat)
body = re.ReplaceAllString(body, r.repl)
}
body = strings.TrimSpace("<div class=\"newsArticle\">\n<a class=\"totop\" href=\"/\">トップへ</a>\n" + strings.TrimSpace(body)) + "\n </div>\n"
return body
}
/* 記事だけが残るまで消す */
func rmbloat(id string, body string, cnf Config) string {
var re *regexp.Regexp
rep := []struct {
pat string
repl string
}{
{`(?s)<!DOCTYPE html>.*?<div class="newsArticle">`, ""},
{`(?s)<!--/newsArticle-->.*?</html>`, ""},
{`(?s)<p class="reactions">.*?</p>`, ""},
{`(?s)<ul class="diaryUtility\d*">.*?</ul>`, ""},
{`(?s)<table>.*?</table>`, ""},
{`(?s)<div class="adsense0.*?</div>`, ""},
{`(?s)<div class="adsense.*?</div>`, ""},
{`www\.?youtube\.com`, "youtube.owacon.moe"},
{`(?s)<div class="subInfo">.*?</div>`, ""},
{`(?s)<div class="additional\d*.*?</div>`, ""},
{`(?s)(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`, "\n"},
{`<!--article_image-->`, ""},
{`<!--/article_image-->`, ""},
{`(?s)<!--.*?-->`, ""},
{`<!--`, ""},
{`(?s)<img src="https://(.*?)"`, `<img src="` + cnf.imgproxy + `/$1"`},
{`https://news-image.mixi.net`, cnf.imgproxy + `/news-image.mixi.net`},
{`https://news.mixi.jp/`, cnf.domain + `/`},
}
for _, r := range rep {
re = regexp.MustCompile(r.pat)
body = re.ReplaceAllString(body, r.repl)
}
body = strings.TrimSpace("<div class=\"newsArticle\">\n<a class=\"totop\" href=\"/\">トップへ</a>\n" + strings.TrimSpace(body))
if id != "" {
body += "\n<p class=\"footer\"><a class=\"tubuyaki-btn\" href=\"/list_quote.pl?news_id=" + id + "&type=voice&sort=feedback_count\">つぶやきを見る</a></p>\n"
}
body += "\n </div>\n"
return body
}

28
spliti.1 Normal file
View File

@@ -0,0 +1,28 @@
.TH spliti 1 spliti\-VERSION
.SH ソフト名
mixi向けプライバシーUI。
.SH 概要
.B spliti
[\fI\,オプション\/\fR] [\fI\,ポート番号\/\fR]
.SH 説明
.PP
splitiはmixi向けプライバシーUIです。
.TP
\fB\-v\fR
バージョンを表示
.TP
\fB\-s [ポート番号]\fR
ポート番号でサーバーを開始デフォルト9930
.TP
\fB\-h\fR
ヘルプを表示
.SH 会話
.PP
IRC: irc.076.ne.jp/6697 #spliti
.br
メーリングリスト: (開発中)
.SH バグ報告
.PP
バグは下記のURLまでご報告下さい
.br
https://gitler.moe/suwako/spliti/issues

89
srv.go Normal file
View File

@@ -0,0 +1,89 @@
package main
import (
"text/template"
"fmt"
"net/http"
"net/url"
"strings"
"log"
"os"
"path/filepath"
)
type Page struct {
Tit, Err, Bdy, Dec, Img, Url, Dom, Ver, Ves string
}
func extractGurl(r *http.Request) (string, error) {
rq := r.URL.RawQuery
q, err := url.QueryUnescape(rq)
if err != nil {
return "URLを受取に失敗", err
}
gurl := strings.Replace(q, "/?url=", "", -1)
return gurl, nil
}
func serv (cnf Config, port int) {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatal(err)
}
err = os.Chdir(dir)
if err != nil {
log.Fatal(err)
}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(cnf.webpath + "/static"))))
ftmpl := []string{cnf.webpath + "/view/index.html", cnf.webpath + "/view/header.html", cnf.webpath + "/view/footer.html"}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 1.xで、URLは「/?url=」が付いたけど、2.0.0からは不要になった
// だから、下記の部分は古いURLの為だ
urls, ok := r.URL.Query()["url"]
if ok && len(urls[0]) > 0 {
http.Redirect(w, r, "/" + urls[0], http.StatusMovedPermanently)
return
}
data := &Page{Ver: version, Ves: strings.ReplaceAll(version, ".", "")}
uri := r.URL.Path
gurl, err := extractGurl(r)
if err != nil {
data.Tit = "エラー"
data.Err = err.Error()
ftmpl[0] = cnf.webpath + "/view/404.html"
}
if uri == "/" {
ftmpl[0] = cnf.webpath + "/view/index.html"
} else {
furl := uri + "?" + gurl
page := get(furl, cnf)
data.Tit = page["title"]
if page["err"] != "" {
data.Err = page["err"]
ftmpl[0] = cnf.webpath + "/view/404.html"
} else {
data.Bdy = page["content"]
data.Img = "/static/logo.jpg"
if isarticle(furl) {
data.Dec = page["desc"]
data.Img = page["img"]
data.Url = cnf.domain + furl
}
ftmpl[0] = cnf.webpath + "/view/news.html"
}
}
tmpl := template.Must(template.ParseFiles(ftmpl[0], ftmpl[1], ftmpl[2]))
tmpl.Execute(w, data)
data = nil
})
fmt.Println(fmt.Sprint("http://" + cnf.ip + ":", port, " でサーバーを実行中。終了するには、CTRL+Cを押して下さい。"))
http.ListenAndServe(fmt.Sprint(cnf.ip + ":", port), nil)
}

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/git.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
static/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

266
static/style.css Normal file
View File

@@ -0,0 +1,266 @@
html {
margin: 0;
}
body {
background: #232629;
color: #fcfcfc;
letter-spacing: 0.05em;
}
a {
color: #ea5fea;
text-decoration: none;
}
h1, h2 {
clear: left;
line-height: 1.5;
margin: 15px 0 10px;
padding: 0 0 0 15px;
border-left: 6px solid #f976de;
font-weight: bold;
}
h1 {
font-size: 200%;
}
h2 {
font-size: 166%;
}
code {
background: #e68be6;
padding: 1px;
border-radius: 4px;
border: 1px #fcfcfc solid;
margin: 4px;
color: #232629;
line-height: 2.5;
}
.article > img {
max-width: 100%;
width: auto;
}
.reactionCountBalloon, .totop, p.reactions, a.tubuyaki-btn {
border: 1px solid #f976de;
border-radius: 2px;
background-color: #232629;
}
.reactionCountBalloon {
margin-left: 8px;
font-size: 60%;
padding: 0 12px;
}
.totop, p.reactions {
padding: 4px;
}
p.reactions {
font-size: 80%;
max-width: 50px;
text-align: center;
}
.newsArticle {
width: calc(100% - 20px);
max-width: 1200px;
background: #31363b;
border: 2px solid #4d4d4d;
border-radius: 10px;
margin: 32px auto;
padding: 8px;
}
.newsTable, .NEWS_pickupNewsBox__contentsArea {
width: calc(100% - 20px);
max-width: 1200px;
background: #462146;
border: 2px solid #f976de;
border-radius: 4px;
margin: 20px auto;
}
.NEWS_pickupNewsBox__contentsArea {
background: #58224d;
}
.NEWS_pickupNewsBox__contentsArea, .NEWS_pickupNewsItem {
display: grid;
grid-template-columns: 3fr 1fr;
grid-gap: 8px;
}
.NEWS_pickupNewsTitle, .NEWS_pickupNewsAuthor {
display: inline-grid;
}
.NEWS_pickupNewsTitle__body, .NEWS_pickupNewsAuthor, tr > td.newsTitle > p {
margin: 2px;
}
.NEWS_pickupNewsBox__contentsArea > div {
background: #462146;
}
.odd {
background: #3c213c;
}
.LEGACY_UI2016_subCategoryNavi > p > a, .pageList01 > div > ul > li,
.pageList01 > ul > li, .pageNavigation02 > p.first,
.pageNavigation02 > p.last, ul.sortTab > li,
.pageList02 > ul > li {
background-color: #4d4d4d;
padding: 4px;
border: 1px solid #c625ef;
border-radius: 2px;
text-decoration: none;
}
.pageList01 > div > ul > li:not([class]):hover,
.pageList01 > ul > li:hover,
ul.sortTab > li:hover,
.pageList02 > ul > li[rel="__prev"]:hover,
.pageList02 > ul > li[rel="__next"]:hover,
li.NEWS_pickupNewsItem:hover,
p.NEWS_pickupItems__moreLink:hover,
li.newCategoryList > a:hover,
.photoNewsArea > a:hover,
.NEWS_photoNewsItems__contentsArea:hover,
a.tubuyaki-btn:hover,
p.first:hover, p.last:hover,
tr.odd:hover, tr.even:hover, .totop:hover {
background-color: #ae6bdb;
}
.pageNavigation02 > p, .pageList01 > ul > li, .pageList02 > ul > li {
padding: 8px !important;
}
.pageNavigation02 > p.none, .pageList01 > ul > li.on, ul.sortTab > li.current,
ul.sortTab > li.current > a {
background-color: #232629;
color: #4d4d4d;
border-color: #4d4d4d;
}
.pageList01 > div > ul, .pageList01 > ul, .pageNavigation02 > p.first,
.pageNavigation02 > p.last, ul.sortTab, .pageList02 > ul > li {
padding-left: unset;
}
.pageList01 > div > ul,
.pageList01 > div > ul > li,
.pageList01 > ul > li,
.pageList02 > ul > li,
.entryList01, .entryList01 > li,
.NEWS_pickupNewsList,
.pageNavigation02 > p.first,
.pageNavigation02 > p.last,
ul.sortTab > li,
ul.voiceList01 .attributes li,
ul.listAction,
h2.newsTitle {
display: inline;
}
.pageList02 {
text-align: center;
padding-top: 8px;
}
.relationNewsDescription {
margin-top: 18px;
}
.relationNewsDescription > p.date {
border-left: 3px solid #f976de;
padding: 0 0 0 8px;
}
ul.voiceList01 {
padding-left: 0;
}
.pageNavigation02 {
display: ruby;
}
.newsCategoryList, .timestamp, .iine, .comment, .item {
list-style: none;
}
.itemIn > .clearfix {
display: flex;
}
ul.voiceList01 > li.item {
background-color: #2f2e2f;
border: 1px dashed #4d4d4d;
margin: 8px 0;
padding: 4px;
}
ul.attributes, ul.listAction {
font-size: 80%;
color: #adadad;
}
ul.attributes {
position: absolute;
white-space: nowrap;
}
ul.listAction {
clear: both;
width: 100%;
margin-top: 5px;
margin-left: 4px;
text-align: right;
}
ul.voiceList01 ul.listAction li {
margin-left: 10px;
font-size: 100%;
display: initial;
}
.NEWS_tempPhoto {
text-align: center;
}
.NEWS_tempPhoto__picture {
max-width: 400px;
max-height: 400px;
margin-bottom: 3px;
}
.NEWS_tempPhoto__captionText {
color: #ccc;
text-align: center;
font-size: 86%;
}
.footer {
text-align: center;
margin-top: 32px;
}
li.iine > img, li.comment > img {
margin-right: 8px;
}
a.tubuyaki-btn {
display: inline-table;
padding: 24px;
}
.nextArticle > a.tubuyaki-btn {
text-align: center;
}

4
view/404.html Normal file
View File

@@ -0,0 +1,4 @@
{{template "header" .}}
<a href="/">トップへ</a>
{{ .Err }}<br />
{{template "footer" .}}

9
view/footer.html Normal file
View File

@@ -0,0 +1,9 @@
{{define "footer"}}
<p class="footer">
<a href="https://technicalsuwako.moe/blog/spliti-{{ .Ves }}/">Spliti {{ .Ver }}</a> |
<a href="https://gitler.moe/suwako/spliti"><img src="/static/git.png" alt="Git"></a> |
<a href="https://076.moe/"></a>
</p>
</body>
</html>
{{end}}

16
view/header.html Normal file
View File

@@ -0,0 +1,16 @@
{{define "header"}}<!DOCTYPE html>
<html lang="ja">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{{if ne .Dec "" }}<meta property="og:title" content="spliti 〜 {{ .Tit }}" />
<meta property="og:type" content="article" />
<meta property="og:description" content="{{ .Dec }}" />
<meta property="og:url" content="{{ .Url }}" />{{end}}
<meta name="thumbnail" content="{{ .Img }}" />
<title>spliti 〜 {{ .Tit }}</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/static/style.css" />
</head>
<body>
{{end}}

21
view/index.html Normal file
View File

@@ -0,0 +1,21 @@
{{template "header" .}}
<div class="newsArticle">
<div class="newsCategoryList">
<div class="heading08">
<h1>ニュースカテゴリ一覧</h1>
</div>
<ul class="newsCategoryList">
<li class="newCategoryList"><a href="/list_news_category.pl?id=7">エンタメ</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=3">トレンド</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=1">社会</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=4">地域</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=9">ゲーム・アニメ</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=8">IT・インターネット</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=6">スポーツ</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=5">海外</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=10">コラム</a></li>
<li class="newCategoryList"><a href="/list_news_category.pl?id=2">ライフスタイル</a></li>
</ul>
</div>
</div>
{{template "footer" .}}

3
view/news.html Normal file
View File

@@ -0,0 +1,3 @@
{{template "header" .}}
{{ .Bdy }}
{{template "footer" .}}