2049BBS

Terminus 2049
  1. 葱侠   回复文章

    从此次疫情暴露出种种白左虚伪面具和西方存在的社会问题

    西雅图那边疫情也很严重吧,去人少一些的乡村还安全一些

  2. covid   回复文章

    tt

    @origin #5

    真是这样。火烧眉毛十万火急了,还在讨论姓资姓社,实在是空谈误国。

  3. natasha 饭姐
    natasha   回复文章

    tt

    @covid #11 @origin 我不认为欧美有把戴口罩这个行为意识形态化的倾向。戴口罩的国家又不止是中国,台湾、香港、日本都在戴口罩。说起戴口罩欧美也不会第一时间直接联想到社会主义中国。因此很难说有意识形态对立问题。

    戴不戴口罩是一种habitus,一种社会行为习惯。中国人就算没有sars和covid-19,也时常会有人戴口罩,起到防风保暖的作用。欧美人士则没有这种习惯。

    欧美国家近年来没有经历过厉害的传染病,他们的经验就是对付流感的经验:勤洗手,少握手。

    因此,我认为,是公共卫生经验的缺乏导致他们对口罩的重视程度不够。

  4. 张怀义   回复文章

    utc 时间 13:30:00 停机维护,大约五分钟恢复正常。

    想想还是php好,改完代码直接ftp上传就生效了,因为是脚本。

    golang因为是编译型语言,每次替换,都需要重启服务。

    最好开个nginx作为前置,需要替换的时候,nginx还可以给个静态网页说在服务器升级,而不是521报错

  5. Misaki KBBL
    Misaki   回复文章

    I just can't stand my m4l3 landl0rd

    内容已删除
    内容已被作者本人或管理员删除。 如有疑问,请点击菜单按钮,查看管理日志以了解原因。
  6. Misaki KBBL
    Misaki   回复文章

    I just can't stand my m4l3 landl0rd

    内容已删除
    内容已被作者本人或管理员删除。 如有疑问,请点击菜单按钮,查看管理日志以了解原因。
  7. Misaki KBBL
    Misaki   发表文章

    I just can't stand my m4l3 landl0rd

    内容已删除
    内容已被作者本人或管理员删除。 如有疑问,请点击菜单按钮,查看管理日志以了解原因。
  8. 小二   回复文章

    2049bbs有onion地址么?刚才看到了

    没有,你肯定眼花。

  9. 张怀义   回复文章

    2049bbs有onion地址么?刚才看到了

    cat /var/lib/tor/hidden_service/hostname
    

    执行上面这个命令,小二应该就可以看到2049bbs.xyz的onion域名了

  10. 张怀义   发表文章

    2049bbs有onion地址么?刚才看到了

    刚才打开2049bbs的时候,提示了一个cfluare打头的onion地址。我还没有保存,这个地址就没了

    有人知道么?

  11. origin   回复文章

    域名和站长隐蔽性有关吗?

    @kyq #16 我自己是用 paypal 绑定了信用卡支付的,不过如果真的想要全匿名的话,可以在实体店用现金给 paypal 充值

  12. origin   回复文章

    中国参与下的全球化与自由主义的悖论

    @厠所管理員 #1 你这个逻辑链条好像不太成立,人均 GDP 低,不过经济总量大,还是能够对世界经济格局产生影响的。

    要是根据品葱的观点,中国现有的经济高速发展不是因为政治体制优越,而是因为经济开放的结果,这样反而比你的 argument 更加合理一点。

  13. 梅菲斯特   回复文章

    发个帖子记录自己看的动画吧

    Heaven's Feel 第二部

    没啥好说的,早就知道剧情了……这一年憋出来一部的……

    战斗画得挺好,就是光污染多了点……

    其实我还挺喜欢姐妹百合CP的,我就是传说中的邪道爱好者= =

  14. 梅菲斯特   回复文章

    发个帖子记录自己看的动画吧

    到底要怎么发标题

    Psycho Pass 3 First Inspector

    ………………这个剧情展开真是。

    全程没啥感想,就是一群人大乱斗……伏笔算是收了吧但是一点都不震撼。

    冲方丁老师你怎么了!!!!

    以及朱妹真是……唉。

    第一部的百合组终于HE了!不容易啊不容易……

  15. kyq   回复文章

    域名和站长隐蔽性有关吗?

    @origin #15 你怎么支付的?

  16. origin   回复文章

    2049增加Google验证码代码

    @小二 #4 @张怀义 #3 请问有没有可能上线帖子编辑功能,或者是如 stackoverflow 一样的错别字编辑功能呢?经常发完帖子之后发现帖子里面有错别字

  17. sorrysorrysorry   发表文章

    囤大米太低端,俺们囤维生素

    先见之明 到时候就知道了

  18. 超维能力   发表文章

    从此次疫情暴露出种种白左虚伪面具和西方存在的社会问题

    此次疫情固然令人恐怖,但是因为中国之前的拖延以及WHO的配合导致了犯有严重幼稚病的欧洲领导层因为误信而作出误判

    进而才造成今日严重的疫情

    此番一切都是源于西方左派社会的幼稚病和无危机感

    在这里特别想举一个切身的例子

    我意大利一位朋友,做医生的,典型左派,极左的那种,后续事情发生让我意识到他是典型的白左

    前段时间这位朋友还和我发信息说,他中了肺炎,但是他说不会怪中国或者中国人什么的,这些是小事而已,流感而已,也提倡说不要对中国人有意见啦,叫我不要批评他们啊,跟他们没关系,都是我们怎么怎么这些措辞非常包容大爱的说法,我很不以为然,因为他所讲的东西忽略了一些很重要的事实,在我看来他的说法空有虚妄的大爱但是缺乏现实依据的盲论,逻辑上也是很有缺陷的,个人直观感受是很无知很蠢的

    但是基于本人朋友友好交流的前提,我虽然不反驳他,但是也委婉地表达了一下不同看法,至少不会配合你说假话,所以便婉转温和地方式说了我的真实看法,他听后当时不同意也不反对

    结果一个礼拜后,他的态度发生次大改变,他再次和我发信息 说他发烧得很难受,天天烧得很严重透不了气喉咙痛得要死各种喊救命之类的,但是突然他说了一句话,一句不可能是左派说的话,他说如果在这次疫情上面,谁要是再帮中国说一句好话,我一定打爆他的头颅,哪怕那个是我妈妈!

    可怕吧,左派因为自身遭遇知道痛苦了,立马放弃自己曾经的坚持站着说话不腰痛的空口白话,变成一个极右份子,这种人便是大家广为传颂的:白左! 这一刻我深深认识到他是白左这个事实,因为之前交流没有特别明显的证据证明他是白左,我对他还抱有善良的愿望,认为应该是糊涂的左派罢了

    没想到当他自己遭受痛苦,当他自己受罪的时候,他原先的标准也全部没了,态度180度大改变,这种双标作态,令我惊觉 “妈的 你是白左!!!"

    借由这事让我联想起现在欧美发生各种排斥和歧视亚裔的疯狂行为,新加坡人,打;泰国人,小孩,不理照打;太可怕了,太疯狂了,原来西方虚妄的左派社会面具下,潜藏着一颗极右的心!令我重新审视过去对于欧美人的认识,真验证中国一句古话:时穷节乃现

    没事时隐藏着很多小人,有事时暴露出很多伪君子

    真正面对困难考验时 人类其实并没有多大的区别

    皆由这次分享,希望大家认识到西方社会的左派原来多数都是“白左”这个事实!

    通过以上各类时间,本人一家已经暂时准备搬到我在西雅图的房子那边居住,尽可能远离可能存在危险的地方

    友情提示:米国的朋友 远离芝加哥,纽约,南方 中部这些地方。对了,还有加州 你问我为什么,因为有很多拉丁裔啊!都是定时炸弹

    欢迎海外的各位分享你们的看法

  19. covid   回复文章

    从此次疫情暴露出种种白左虚伪面具和西方存在的社会问题

    这次好像是共和党在淡化疫情吧。选民也是共和党的选民比较没把COVID-19当一回事。水军这会儿还批白左,有点硬干的感觉,太不识相。

  20. 张怀义   回复文章

    rss 不输出树洞、水区和垃圾场内容

    树洞、水区和垃圾场at无效

    https://github.com/Terminus2049/2049bbs/issues/8

    修改代码 https://github.com/Terminus2049/2049bbs/blob/master/controller/article.go

    package controller
    
    import (
    	"crypto/md5"
    	"encoding/hex"
    	"encoding/json"
    	"html/template"
    	"math/rand"
    	"net/http"
    	"strconv"
    	"strings"
    	"time"
    
    	"github.com/ego008/youdb"
    	"github.com/rs/xid"
    	"github.com/terminus2049/2049bbs/model"
    	"github.com/terminus2049/2049bbs/util"
    	"goji.io/pat"
    )
    
    func (h *BaseHandler) ArticleAdd(w http.ResponseWriter, r *http.Request) {
    	cid := pat.Param(r, "cid")
    	_, err := strconv.Atoi(cid)
    	if err != nil {
    		w.Write([]byte(`{"retcode":400,"retmsg":"cid type err"}`))
    		return
    	}
    
    	currentUser, _ := h.CurrentUser(w, r)
    	if currentUser.Id == 0 {
    		w.Write([]byte(`{"retcode":401,"retmsg":"authored err"}`))
    		return
    	}
    	if currentUser.Flag < 5 {
    		var msg string
    		if currentUser.Flag == 1 {
    			msg = "注册验证中,等待管理员通过"
    		} else {
    			msg = "您已被禁用"
    		}
    		w.Write([]byte(`{"retcode":401,"retmsg":"` + msg + `"}`))
    		return
    	}
    
    	db := h.App.Db
    
    	cobj, err := model.CategoryGetById(db, cid)
    	if err != nil {
    		w.Write([]byte(`{"retcode":404,"retmsg":"` + err.Error() + `"}`))
    		return
    	}
    
    	type pageData struct {
    		PageData
    		Cobj      model.Category
    		MainNodes []model.CategoryMini
    	}
    
    	tpl := h.CurrentTpl(r)
    	evn := &pageData{}
    	evn.SiteCf = h.App.Cf.Site
    	evn.Title = "发表文章"
    	evn.IsMobile = tpl == "mobile"
    	evn.CurrentUser = currentUser
    	evn.ShowSideAd = true
    	evn.PageName = "article_add"
    
    	evn.Cobj = cobj
    	evn.MainNodes = model.CategoryGetMain(db, cobj)
    
    	h.SetCookie(w, "token", xid.New().String(), 1)
    	h.Render(w, tpl, evn, "layout.html", "articlecreate.html")
    }
    
    func (h *BaseHandler) ArticleAddPost(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    
    	token := h.GetCookie(r, "token")
    	if len(token) == 0 {
    		w.Write([]byte(`{"retcode":400,"retmsg":"token cookie missed"}`))
    		return
    	}
    
    	currentUser, _ := h.CurrentUser(w, r)
    	if currentUser.Id == 0 {
    		w.Write([]byte(`{"retcode":401,"retmsg":"authored require"}`))
    		return
    	}
    	if currentUser.Flag < 5 {
    		w.Write([]byte(`{"retcode":403,"retmsg":"user flag err"}`))
    		return
    	}
    
    	type recForm struct {
    		Act     string `json:"act"`
    		Cid     uint64 `json:"cid"`
    		Title   string `json:"title"`
    		Content string `json:"content"`
    	}
    
    	decoder := json.NewDecoder(r.Body)
    	var rec recForm
    	err := decoder.Decode(&rec)
    	if err != nil {
    		w.Write([]byte(`{"retcode":400,"retmsg":"json Decode err:` + err.Error() + `"}`))
    		return
    	}
    	defer r.Body.Close()
    
    	rec.Title = strings.TrimSpace(rec.Title)
    	rec.Content = strings.TrimSpace(rec.Content)
    
    	db := h.App.Db
    	if rec.Act == "preview" {
    		tmp := struct {
    			normalRsp
    			Html string `json:"html"`
    		}{
    			normalRsp{200, ""},
    			util.ContentFmt(db, rec.Content),
    		}
    		json.NewEncoder(w).Encode(tmp)
    		return
    	}
    
    	// check title
    	hash := md5.Sum([]byte(rec.Title))
    	titleMd5 := hex.EncodeToString(hash[:])
    	if db.Hget("title_md5", []byte(titleMd5)).State == "ok" {
    		w.Write([]byte(`{"retcode":403,"retmsg":"title has existed"}`))
    		return
    	}
    
    	now := uint64(time.Now().UTC().Unix())
    	scf := h.App.Cf.Site
    
    	if currentUser.Flag < 99 && currentUser.LastPostTime > 0 {
    		if (now - currentUser.LastPostTime) < uint64(scf.PostInterval) {
    			w.Write([]byte(`{"retcode":403,"retmsg":"PostInterval limited"}`))
    			return
    		}
    	}
    
    	if rec.Cid == 0 || len(rec.Title) == 0 {
    		w.Write([]byte(`{"retcode":400,"retmsg":"missed args"}`))
    		return
    	}
    	if len(rec.Title) > scf.TitleMaxLen {
    		w.Write([]byte(`{"retcode":403,"retmsg":"TitleMaxLen limited"}`))
    		return
    	}
    	if len(rec.Content) > scf.ContentMaxLen {
    		w.Write([]byte(`{"retcode":403,"retmsg":"ContentMaxLen limited"}`))
    		return
    	}
    
    	newAid, _ := db.HnextSequence("article")
    	aobj := model.Article{
    		Id:       newAid,
    		Uid:      currentUser.Id,
    		Cid:      rec.Cid,
    		Title:    rec.Title,
    		Content:  rec.Content,
    		AddTime:  now,
    		EditTime: now,
    		ClientIp: "",
    	}
    
    	jb, _ := json.Marshal(aobj)
    	aidB := youdb.I2b(newAid)
    	db.Hset("article", aidB, jb)
    	// 总文章列表
    	ignorenodes := scf.NotHomeNodeIds
    	if len(ignorenodes) > 0 {
    		for _, node := range strings.Split(ignorenodes, ",") {
    			node, err := strconv.Atoi(node)
    			if err == nil && aobj.Cid != uint64(node) {
    				db.Zset("article_timeline", aidB, aobj.EditTime)
    			}
    		}
    	}
    
    	// 分类文章列表
    	db.Zset("category_article_timeline:"+strconv.FormatUint(aobj.Cid, 10), aidB, aobj.EditTime)
    	// 用户文章列表
    	db.Hset("user_article_timeline:"+strconv.FormatUint(aobj.Uid, 10), youdb.I2b(aobj.Id), []byte(""))
    	// 分类下文章数
    	db.Zincr("category_article_num", youdb.I2b(aobj.Cid), 1)
    
    	currentUser.LastPostTime = now
    	currentUser.Articles++
    
    	jb, _ = json.Marshal(currentUser)
    	db.Hset("user", youdb.I2b(aobj.Uid), jb)
    
    	// title md5
    	db.Hset("title_md5", []byte(titleMd5), aidB)
    
    	// send task work
    	// get tag from title
    	if scf.AutoGetTag {
    		db.Hset("task_to_get_tag", aidB, []byte(rec.Title))
    	}
    
    	// @ somebody in content
    if rec.Cid != 19 && rec.Cid != 20 {
    	sbs := util.GetMention(rec.Content,
    		[]string{currentUser.Name, strconv.FormatUint(currentUser.Id, 10)})
    
    	aid := strconv.FormatUint(newAid, 10)
    	for _, sb := range sbs {
    		var sbObj model.User
    		sbu, err := strconv.ParseUint(sb, 10, 64)
    		if err != nil {
    			// @ user name
    			sbObj, err = model.UserGetByName(db, strings.ToLower(sb))
    		} else {
    			// @ user id
    			sbObj, err = model.UserGetById(db, sbu)
    		}
    
    		if err == nil {
    			if len(sbObj.Notice) > 0 {
    				aidList := util.SliceUniqStr(strings.Split(aid+","+sbObj.Notice, ","))
    				if len(aidList) > 100 {
    					aidList = aidList[:100]
    				}
    				sbObj.Notice = strings.Join(aidList, ",")
    				sbObj.NoticeNum = len(aidList)
    			} else {
    				sbObj.Notice = aid
    				sbObj.NoticeNum = 1
    			}
    			jb, _ := json.Marshal(sbObj)
    			db.Hset("user", youdb.I2b(sbObj.Id), jb)
    		}
    }
    	}
    
    	h.DelCookie(w, "token")
    
    	tmp := struct {
    		normalRsp
    		Aid uint64 `json:"aid"`
    	}{
    		normalRsp{200, "ok"},
    		aobj.Id,
    	}
    	json.NewEncoder(w).Encode(tmp)
    }
    
    func (h *BaseHandler) ArticleHomeList(w http.ResponseWriter, r *http.Request) {
    	btn, key, score := r.FormValue("btn"), r.FormValue("key"), r.FormValue("score")
    	if len(key) > 0 {
    		_, err := strconv.ParseUint(key, 10, 64)
    		if err != nil {
    			w.Write([]byte(`{"retcode":400,"retmsg":"key type err"}`))
    			return
    		}
    	}
    	if len(score) > 0 {
    		_, err := strconv.ParseUint(score, 10, 64)
    		if err != nil {
    			w.Write([]byte(`{"retcode":400,"retmsg":"score type err"}`))
    			return
    		}
    	}
    
    	cmd := "zrscan"
    	if btn == "prev" {
    		cmd = "zscan"
    	}
    
    	db := h.App.Db
    	scf := h.App.Cf.Site
    	pageInfo := model.ArticleList(db, cmd, "article_timeline", key, score, scf.HomeShowNum*2, scf.TimeZone, scf.NotHomeNodeIds)
    	currentUser, _ := h.CurrentUser(w, r)
    
    	// 首页第二栏节点
    	if currentUser.Theme != "2" {
    		cid := scf.HomeNode
    		cobj, err := model.CategoryGetById(db, cid)
    		if err == nil {
    			cobj.Articles = db.Zget("category_article_num", youdb.I2b(cobj.Id)).Uint64()
    			pageInfo2 := model.ArticleList(db, cmd, "category_article_timeline:"+cid, key, score, scf.HomeShowNum, scf.TimeZone, "")
    			pageInfo.Items = append(pageInfo.Items, pageInfo2.Items...)
    		}
    	}
    
    	type siteInfo struct {
    		Days     int
    		UserNum  uint64
    		NodeNum  uint64
    		TagNum   uint64
    		PostNum  uint64
    		ReplyNum uint64
    	}
    
    	type pageData struct {
    		PageData
    		SiteInfo     siteInfo
    		PageInfo     model.ArticlePageInfo
    		Links        []model.Link
    		Announcement string
    		Proverb      template.HTML
    	}
    
    	si := siteInfo{}
    	rs := db.Hget("count", []byte("site_create_time"))
    	var siteCreateTime uint64
    	if rs.State == "ok" {
    		siteCreateTime = rs.Data[0].Uint64()
    	} else {
    		rs2 := db.Hscan("user", []byte(""), 1)
    		if rs2.State == "ok" {
    			user := model.User{}
    			json.Unmarshal(rs2.Data[1], &user)
    			siteCreateTime = user.RegTime
    		} else {
    			siteCreateTime = uint64(time.Now().UTC().Unix())
    		}
    		db.Hset("count", []byte("site_create_time"), youdb.I2b(siteCreateTime))
    	}
    	then := time.Unix(int64(siteCreateTime), 0)
    	diff := time.Now().UTC().Sub(then)
    	si.Days = int(diff.Hours()/24) + 1
    	si.UserNum = db.Hsequence("user")
    	si.NodeNum = db.Hsequence("category")
    	si.TagNum = db.Hsequence("tag")
    	si.PostNum = db.Hsequence("article")
    	si.ReplyNum = db.Hget("count", []byte("comment_num")).Uint64()
    
    	// fix
    	if si.NodeNum == 0 {
    		newCid, err2 := db.HnextSequence("category")
    		if err2 == nil {
    			cobj := model.Category{
    				Id:    newCid,
    				Name:  "默认分类",
    				About: "默认第一个分类",
    			}
    			jb, _ := json.Marshal(cobj)
    			db.Hset("category", youdb.I2b(cobj.Id), jb)
    			si.NodeNum = 1
    		}
    		// link
    		model.LinkSet(db, model.Link{
    			Name:  "youBBS",
    			Url:   "https://www.youbbs.org",
    			Score: 100,
    		})
    	}
    
    	tpl := h.CurrentTpl(r)
    	evn := &pageData{}
    	evn.SiteCf = scf
    	evn.Title = scf.Name
    	evn.Keywords = evn.Title
    	evn.Description = scf.Desc
    	evn.IsMobile = tpl == "mobile"
    	evn.CurrentUser = currentUser
    	evn.ShowSideAd = true
    	evn.PageName = "home"
    	evn.HotNodes = model.CategoryHot(db, scf.CategoryShowNum, scf.MustLoginNodeIds)
    
    	if currentUser.IgnoreNode != "" {
    		for _, node := range strings.Split(currentUser.IgnoreNode, ",") {
    			node, err := strconv.ParseUint(node, 10, 64)
    
    			if err != nil {
    				w.Write([]byte(`{"retcode":400,"retmsg":"忽略节点id应为整数,请在设置中检查。"}`))
    				return
    			}
    
    			for i := 0; i < len(pageInfo.Items); i++ {
    				if pageInfo.Items[i].Cid == node {
    					pageInfo.Items = append(pageInfo.Items[:i], pageInfo.Items[i+1:]...)
    					i--
    				}
    			}
    		}
    	}
    
    	if currentUser.IgnoreUser != "" {
    		for _, uid := range strings.Split(currentUser.IgnoreUser, ",") {
    			uid, err := strconv.ParseUint(uid, 10, 64)
    
    			if err != nil {
    				w.Write([]byte(`{"retcode":400,"retmsg":"忽略用户id应为整数,请在设置中检查。"}`))
    				return
    			}
    
    			for i := 0; i < len(pageInfo.Items); i++ {
    				if pageInfo.Items[i].Uid == uid {
    					pageInfo.Items = append(pageInfo.Items[:i], pageInfo.Items[i+1:]...)
    					i--
    				}
    			}
    		}
    	}
    
    	// 类似 solidot 格言
    	if !evn.IsMobile {
    		proverbs := model.CommentList(db, "hscan", "article_comment:"+scf.ProverbId, "", 500, scf.TimeZone, false)
    		// 剔除折叠的回复
    		for i := 0; i < len(proverbs.Items); i++ {
    			if proverbs.Items[i].Fold {
    				proverbs.Items = append(proverbs.Items[:i], proverbs.Items[i+1:]...)
    				i--
    			}
    		}
    
    		rand.Seed(time.Now().Unix())
    		if len(proverbs.Items) > 0 {
    			evn.Proverb = proverbs.Items[rand.Intn(len(proverbs.Items))].ContentFmt
    		}
    	}
    
    	evn.SiteInfo = si
    	evn.PageInfo = pageInfo
    	evn.Links = model.LinkList(db, false)
    
    	// 公告板功能
    	uobj, err := model.UserGetById(db, 1)
    	if err != nil {
    		evn.Announcement = "公告板,可修改1号用户的个人简介进行修改。"
    	} else {
    		evn.Announcement = uobj.About
    	}
    
    	if currentUser.Theme == "2" && !evn.IsMobile {
    		h.Render(w, tpl, evn, "layout.html", "index2.html")
    	} else {
    		h.Render(w, tpl, evn, "layout.html", "index.html")
    	}
    }
    
    func (h *BaseHandler) ArticleDetail(w http.ResponseWriter, r *http.Request) {
    	btn, key, score := r.FormValue("btn"), r.FormValue("key"), r.FormValue("score")
    	if len(key) > 0 {
    		_, err := strconv.ParseUint(key, 10, 64)
    		if err != nil {
    			w.Write([]byte(`{"retcode":400,"retmsg":"key type err"}`))
    			return
    		}
    	}
    	if len(score) > 0 {
    		_, err := strconv.ParseUint(score, 10, 64)
    		if err != nil {
    			w.Write([]byte(`{"retcode":400,"retmsg":"score type err"}`))
    			return
    		}
    	}
    
    	aid := pat.Param(r, "aid")
    	_, err := strconv.Atoi(aid)
    	if err != nil {
    		w.Write([]byte(`{"retcode":400,"retmsg":"aid type err"}`))
    		return
    	}
    
    	cmd := "hscan"
    	if btn == "prev" {
    		cmd = "hrscan"
    	}
    
    	db := h.App.Db
    	scf := h.App.Cf.Site
    	aobj, err := model.ArticleGetById(db, aid)
    	if err != nil {
    		w.Write([]byte(err.Error()))
    		return
    	}
    	currentUser, _ := h.CurrentUser(w, r)
    
    	if len(currentUser.Notice) > 0 && len(currentUser.Notice) >= len(aid) {
    		if len(aid) == len(currentUser.Notice) && aid == currentUser.Notice {
    			currentUser.Notice = ""
    			currentUser.NoticeNum = 0
    			jb, _ := json.Marshal(currentUser)
    			db.Hset("user", youdb.I2b(currentUser.Id), jb)
    		} else {
    			subStr := "," + aid + ","
    			newNotice := "," + currentUser.Notice + ","
    			if strings.Index(newNotice, subStr) >= 0 {
    				currentUser.Notice = strings.Trim(strings.Replace(newNotice, subStr, "", 1), ",")
    				currentUser.NoticeNum = len(strings.Split(currentUser.Notice, ","))
    				jb, _ := json.Marshal(currentUser)
    				db.Hset("user", youdb.I2b(currentUser.Id), jb)
    			}
    		}
    	}
    
    	if aobj.Hidden && currentUser.Flag < 1 {
    		w.WriteHeader(http.StatusNotFound)
    		w.Write([]byte(`{"retcode":404,"retmsg":"仅登录用户可见"}`))
    		return
    	}
    	cobj, err := model.CategoryGetById(db, strconv.FormatUint(aobj.Cid, 10))
    	if err != nil {
    		w.Write([]byte(err.Error()))
    		return
    	}
    
    	if cobj.Hidden && currentUser.Flag < 1 {
    		w.WriteHeader(http.StatusNotFound)
    		w.Write([]byte(`{"retcode":404,"retmsg":"仅登录用户可见"}`))
    		return
    	}
    
    	if cobj.Id == 20 && currentUser.Flag < 99 {
    		w.WriteHeader(http.StatusNotFound)
    		w.Write([]byte(`{"retcode":404,"retmsg":"请勿在垃圾场逗留"}`))
    		return
    	}
    
    	// Authorized
    	if scf.Authorized && currentUser.Flag < 5 {
    		w.WriteHeader(http.StatusUnauthorized)
    		w.Write([]byte(`{"retcode":401,"retmsg":"Unauthorized"}`))
    		return
    	}
    
    	cobj.Articles = db.Zget("category_article_num", youdb.I2b(cobj.Id)).Uint64()
    	pageInfo := model.CommentList(db, cmd, "article_comment:"+aid, key, scf.CommentListNum, scf.TimeZone, currentUser.IgnoreLimitedUsers)
    
    	if currentUser.IgnoreUser != "" {
    		for _, uid := range strings.Split(currentUser.IgnoreUser, ",") {
    			uid, err := strconv.ParseUint(uid, 10, 64)
    
    			if err != nil {
    				w.Write([]byte(`{"retcode":400,"retmsg":"type err"}`))
    				return
    			}
    
    			for i := 0; i < len(pageInfo.Items); i++ {
    				if pageInfo.Items[i].Uid == uid {
    					pageInfo.Items = append(pageInfo.Items[:i], pageInfo.Items[i+1:]...)
    					i--
    				}
    			}
    		}
    	}
    
    	type articleForDetail struct {
    		model.Article
    		ContentFmt  template.HTML
    		TagStr      template.HTML
    		Name        string
    		Avatar      string
    		Views       uint64
    		AddTimeFmt  string
    		EditTimeFmt string
    	}
    
    	type pageData struct {
    		PageData
    		Aobj     articleForDetail
    		Author   model.User
    		Cobj     model.Category
    		Relative model.ArticleRelative
    		PageInfo model.CommentPageInfo
    		Views    uint64
    	}
    
    	tpl := h.CurrentTpl(r)
    	evn := &pageData{}
    	evn.SiteCf = scf
    	evn.Title = aobj.Title + " - " + cobj.Name + " - " + scf.Name
    	evn.Keywords = aobj.Tags
    	evn.Description = cobj.Name + " - " + aobj.Title + " - " + aobj.Tags
    	evn.IsMobile = tpl == "mobile"
    
    	evn.CurrentUser = currentUser
    	evn.ShowSideAd = true
    	evn.PageName = "article_detail"
    	evn.HotNodes = model.CategoryHot(db, scf.CategoryShowNum, scf.MustLoginNodeIds)
    
    	author, _ := model.UserGetById(db, aobj.Uid)
    	viewsNum, _ := db.Hincr("article_views", youdb.I2b(aobj.Id), 1)
    	evn.Aobj = articleForDetail{
    		Article:     aobj,
    		ContentFmt:  template.HTML(util.ContentFmt(db, aobj.Content)),
    		Name:        author.Name,
    		Avatar:      author.Avatar,
    		Views:       viewsNum,
    		AddTimeFmt:  util.TimeFmt(aobj.AddTime, "2006-01-02", scf.TimeZone),
    		EditTimeFmt: util.TimeFmt(aobj.EditTime, "2006-01-02", scf.TimeZone),
    	}
    
    	if len(aobj.Tags) > 0 {
    		var tags []string
    		for _, v := range strings.Split(aobj.Tags, ",") {
    			tags = append(tags, `<a href="/tag/`+v+`">`+v+`</a>`)
    		}
    		evn.Aobj.TagStr = template.HTML(strings.Join(tags, ", "))
    	}
    
    	evn.Cobj = cobj
    	evn.Relative = model.ArticleGetRelative(db, aobj.Id, aobj.Tags)
    	evn.PageInfo = pageInfo
    
    	token := h.GetCookie(r, "token")
    	if len(token) == 0 {
    		token := xid.New().String()
    		h.SetCookie(w, "token", token, 1)
    	}
    
    	h.Render(w, tpl, evn, "layout.html", "article.html")
    }
    
    func (h *BaseHandler) ArticleDetailPost(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    	token := h.GetCookie(r, "token")
    	if len(token) == 0 {
    		w.Write([]byte(`{"retcode":400,"retmsg":"token cookie missed"}`))
    		return
    	}
    
    	aid := pat.Param(r, "aid")
    	_, err := strconv.Atoi(aid)
    	if err != nil {
    		w.Write([]byte(`{"retcode":400,"retmsg":"aid type err:` + err.Error() + `"}`))
    		return
    	}
    
    	type recForm struct {
    		Act     string `json:"act"`
    		Link    string `json:"link"`
    		Content string `json:"content"`
    	}
    
    	type response struct {
    		normalRsp
    		Content string        `json:"content"`
    		Html    template.HTML `json:"html"`
    	}
    
    	decoder := json.NewDecoder(r.Body)
    	var rec recForm
    	err = decoder.Decode(&rec)
    	if err != nil {
    		w.Write([]byte(`{"retcode":400,"retmsg":"json Decode err:` + err.Error() + `"}`))
    		return
    	}
    	defer r.Body.Close()
    
    	db := h.App.Db
    	rsp := response{}
    
    	if rec.Act == "link_click" {
    		rsp.Retcode = 200
    		if len(rec.Link) > 0 {
    			hash := md5.Sum([]byte(rec.Link))
    			urlMd5 := hex.EncodeToString(hash[:])
    			bn := "article_detail_token"
    			clickKey := []byte(token + ":click:" + urlMd5)
    			if db.Zget(bn, clickKey).State == "ok" {
    				w.Write([]byte(`{"retcode":403,"retmsg":"err"}`))
    				return
    			}
    			db.Zset(bn, clickKey, uint64(time.Now().UTC().Unix()))
    			db.Hincr("url_md5_click", []byte(urlMd5), 1)
    
    			w.Write([]byte(`{"retcode":200,"retmsg":"ok"}`))
    			return
    		}
    	} else if rec.Act == "comment_preview" {
    		rsp.Retcode = 200
    		rsp.Html = template.HTML(util.ContentFmt(db, rec.Content))
    	} else if rec.Act == "comment_submit" {
    		timeStamp := uint64(time.Now().UTC().Unix())
    		currentUser, _ := h.CurrentUser(w, r)
    		if currentUser.Flag < 5 {
    			w.Write([]byte(`{"retcode":403,"retmsg":"forbidden"}`))
    			return
    		}
    		if (timeStamp - currentUser.LastReplyTime) < uint64(h.App.Cf.Site.CommentInterval) {
    			w.Write([]byte(`{"retcode":403,"retmsg":"out off comment interval"}`))
    			return
    		}
    		aobj, err := model.ArticleGetById(db, aid)
    		if err != nil {
    			w.Write([]byte(`{"retcode":404,"retmsg":"not found"}`))
    			return
    		}
    		if aobj.CloseComment {
    			w.Write([]byte(`{"retcode":403,"retmsg":"comment forbidden"}`))
    			return
    		}
    		commentId, _ := db.HnextSequence("article_comment:" + aid)
    
    		scf := h.App.Cf.Site
    
    		if currentUser.Flag == 99 && (strings.Contains(rec.Content, "警告一次") ||
    			strings.Contains(rec.Content, "管理员回复")) {
    			currentUser, err = model.UserGetById(db, uint64(scf.AdminBotId))
    			if err != nil {
    				w.Write([]byte(`{"retcode":400,"retmsg":"json Decode err:` + err.Error() + `"}`))
    				return
    			}
    		}
    
    		if currentUser.Flag >= 96 && strings.Contains(rec.Content, "匿名者说") {
    			currentUser, err = model.UserGetById(db, uint64(scf.AnonymousBotId))
    			if err != nil {
    				w.Write([]byte(`{"retcode":400,"retmsg":"json Decode err:` + err.Error() + `"}`))
    				return
    			}
    		}
    
    		obj := model.Comment{
    			Id:       commentId,
    			Aid:      aobj.Id,
    			Uid:      currentUser.Id,
    			Content:  rec.Content,
    			AddTime:  timeStamp,
    			ClientIp: "",
    		}
    		jb, _ := json.Marshal(obj)
    
    		db.Hset("article_comment:"+aid, youdb.I2b(obj.Id), jb) // 文章评论bucket
    		db.Hincr("count", []byte("comment_num"), 1)            // 评论总数
    		// 用户回复文章列表
    		db.Zset("user_article_reply:"+strconv.FormatUint(obj.Uid, 10), youdb.I2b(obj.Aid), obj.AddTime)
    
    		// 更新文章列表时间
    
    		aobj.Comments = commentId
    		aobj.RUid = currentUser.Id
    		aobj.EditTime = timeStamp
    		jb2, _ := json.Marshal(aobj)
    		db.Hset("article", youdb.I2b(aobj.Id), jb2)
    
    		currentUser.LastReplyTime = timeStamp
    		currentUser.Replies += 1
    		jb3, _ := json.Marshal(currentUser)
    		db.Hset("user", youdb.I2b(currentUser.Id), jb3)
    
    		// 不顶帖用户组
    		if currentUser.Flag != 6 {
    			// 总文章列表
    			db.Zset("article_timeline", youdb.I2b(aobj.Id), timeStamp)
    			// 分类文章列表
    			db.Zset("category_article_timeline:"+strconv.FormatUint(aobj.Cid, 10), youdb.I2b(aobj.Id), timeStamp)
    		}
    
    		// @ somebody in comment & topic author
    if aobj.Cid != 19 && aobj.Cid != 20 {
    		sbs := util.GetMention("@"+strconv.FormatUint(aobj.Uid, 10)+" "+rec.Content,
    			[]string{currentUser.Name, strconv.FormatUint(currentUser.Id, 10)})
    		for _, sb := range sbs {
    			var sbObj model.User
    			sbu, err := strconv.ParseUint(sb, 10, 64)
    			if err != nil {
    				// @ user name
    				sbObj, err = model.UserGetByName(db, strings.ToLower(sb))
    			} else {
    				// @ user id
    				sbObj, err = model.UserGetById(db, sbu)
    			}
    
    			if err == nil {
    				if len(sbObj.Notice) > 0 {
    					aidList := util.SliceUniqStr(strings.Split(aid+","+sbObj.Notice, ","))
    					if len(aidList) > 100 {
    						aidList = aidList[:100]
    					}
    					sbObj.Notice = strings.Join(aidList, ",")
    					sbObj.NoticeNum = len(aidList)
    				} else {
    					sbObj.Notice = aid
    					sbObj.NoticeNum = 1
    				}
    				jb, _ := json.Marshal(sbObj)
    				db.Hset("user", youdb.I2b(sbObj.Id), jb)
    			}
    		}
    }
    
    		rsp.Retcode = 200
    	}
    
    	json.NewEncoder(w).Encode(rsp)
    }
    
    func (h *BaseHandler) ContentPreviewPost(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    	token := h.GetCookie(r, "token")
    	if len(token) == 0 {
    		w.Write([]byte(`{"retcode":400,"retmsg":"token cookie missed"}`))
    		return
    	}
    
    	currentUser, _ := h.CurrentUser(w, r)
    	if currentUser.Flag < 5 {
    		w.Write([]byte(`{"retcode":403,"retmsg":"forbidden"}`))
    		return
    	}
    
    	type recForm struct {
    		Act     string `json:"act"`
    		Link    string `json:"link"`
    		Content string `json:"content"`
    	}
    
    	type response struct {
    		normalRsp
    		Content string        `json:"content"`
    		Html    template.HTML `json:"html"`
    	}
    
    	decoder := json.NewDecoder(r.Body)
    	var rec recForm
    	err := decoder.Decode(&rec)
    	if err != nil {
    		w.Write([]byte(`{"retcode":400,"retmsg":"json Decode err:` + err.Error() + `"}`))
    		return
    	}
    	defer r.Body.Close()
    
    	db := h.App.Db
    	rsp := response{}
    
    	if rec.Act == "preview" && len(rec.Content) > 0 {
    		rsp.Retcode = 200
    		rsp.Html = template.HTML(util.ContentFmt(db, rec.Content))
    	}
    	json.NewEncoder(w).Encode(rsp)
    }
    
  21. origin   回复文章

    专家预测,疫情之后,全球将有500万人起诉中国政府

    @该干嘛继续干 #3 感谢说明,我是第一次听闻这一概念。不过真的要继续执行大概还有不少程序上的困难,还是很麻烦的。

    @笑翻江山 #4 虽然没有同这一条新闻完全一样的报道,不过我之前看到过美国有律所计划起诉中国政府,可能这一报道是按照那一条报道夸张化的

  22. 小二   回复文章

    rss 不输出树洞、水区和垃圾场内容

    @张怀义 #2 你为啥发不了帖子?

    的确很冗余,当时只是为了快速解决问题嘛,在原有全部帖子输出的基础上再搞一个。但现在木已成舟,说啥都晚了。

  23. 大风刮过地皮   回复文章

    以五毛靠量挤占墙外站来反思,怎样保障一个站不被五毛(与低端)冲坏?

    当你把所有为中共说话的都归为五毛,和官方引导话题,这种时候就说明这个网站本身就已经有立场了,所谓的公开公平的交流其实已经变成一个有政治倾向圈子的自嗨了。因为除了五毛党,还有一些境外别有用心的言论,还有本身年龄较低容易受人引导思想的用户,这些群体你根本筛选不出来,如果你只是有目的地去限制某一类,就势必变成暴力审查了。舆论战已经很长时间了,任何一方都不会有明显的水军特征,从技术上是真的无法鉴别,除非抬高门槛。如果像47楼那样说的,那不还是国内的审查机制,只不过是反过来的,这样建立起的论坛,说白了就是一个反华势力论坛了,根本不是什么自由与平等了

  24. 张怀义   回复文章

    rss 不输出树洞、水区和垃圾场内容

    @小二 #3 不知道,点了“发表”,什么反应都没有。。

    算了,有空把那些代码都改了吧。我是手机,没法git clone完整的项目,所以我都是怎么改简单,怎么来的

  25. 小二   回复文章

    rss 不输出树洞、水区和垃圾场内容

    @张怀义 #5 我也发现这个问题了,打开控制台,是能看到反馈的,一般是标题过长等原因,但是不知道为啥没有弹出来。这个是js的问题,i hate js。

  26. 张怀义   回复文章

    rss 不输出树洞、水区和垃圾场内容

    @翻车了 #1 我发不了帖子了!!!

    我觉得,feed那段代码,实在不敢恭维!

    feed下,走的是 ArticleFeedList

    feed节点下,走的是 ArticleFeedCategoryList

    我也想在ArticleFeedList里写,但是我觉得很多代码完全是重复而且冗余

    如果我来改,我是从route层就已经改掉了,后面的代码会精简很多

  27. tongtingwah   回复文章

    专家预测,疫情之后,全球将有500万人起诉中国政府

    我也在找,什么时候Jack Rives说过这些话。

  28. 笑翻江山   回复文章

    专家预测,疫情之后,全球将有500万人起诉中国政府

    无信息来源,会有个后果。

    用标题一搜,

    “专家预测,疫情之后,全球将有500万人起诉中国政府”,出自2049。

    个人认为是有损2049声誉的。

  29. 翻车了   回复文章

    rss 不输出树洞、水区和垃圾场内容

    直接在 ArticleFeedList 里处理会更好。可以看下提交记录

    https://github.com/Terminus2049/2049bbs/commits/master

    我会把issue完善下,免得你做重复工作

  30. 翻车了   发表文章

    【每日翻车新闻搬运】vol 285:这都可以嗑cp????

    https://www.reddit.com/r/hanren/comments/fsuax5

    【翻车新闻】vol 285:这都可以嗑cp????

    请问大家对翻车新闻有什么意见???感觉最近点击率不怎么高啊

    1 瑞典又乳了


    2 要开始打倒了?



    3 尘肺病纪录片的导演豆瓣广播




    4 ???这是在嗑cp?




    5 虽然不是同一个时间,但是同一个厕所。。。


    6 这样吧,下次她免费,收费


    7 奇行种


    8 对 都是美国人害的!


    9 知乎敢死队



    10 原来也是个有前科的啊



    11 疯了,真的疯了



    12 你说呢?接盘侠?

    本次新闻播放完毕,谢谢欣赏!再次感谢原OP s1refugee的汇总!

  31. 张怀义   发表文章

    rss 不输出树洞、水区和垃圾场内容

    https://github.com/Terminus2049/2049bbs/issues/7

    修改代码路径 https://github.com/Terminus2049/2049bbs/blob/master/controller/feed.go

    package controller
    
    
    
    import (
    
    	"net/http"
    
    	"strconv"
    
    	"text/template"
    
    
    
    	"github.com/terminus2049/2049bbs/model"
    
    	"goji.io/pat"
    
    )
    
    
    
    func (h *BaseHandler) FeedHandler(w http.ResponseWriter, r *http.Request) {
    
    	w.Header().Set("Content-Type", "application/atom+xml; charset=utf-8")
    
    
    
    	scf := h.App.Cf.Site
    
    
    
    	var feed = `<?xml version="1.0" encoding="utf-8"?>
    
    <feed xmlns="http://www.w3.org/2005/Atom">
    
    	<title>` + scf.Name + `</title>
    
    	<link rel="self" type="application/atom+xml" href="` + scf.MainDomain + `/feed"/>
    
    	<link rel="hub" href="https://pubsubhubbub.appspot.com"/>
    
    	<updated>{ {.Update} }</updated>
    
    	<id>` + scf.MainDomain + `/feed</id>
    
    	<author>
    
    		<name>` + scf.Name + `</name>
    
    	</author>
    
    	{ {range $_, $item := .Items} }
    
    	<entry>
    
    		<title>{ {$item.Title} }</title>
    
    		<id>` + scf.MainDomain + `/t/{ {$item.Id} }</id>
    
    		<link rel="alternate" type="text/html" href="` + scf.MainDomain + `/t/{ {$item.Id} }" />
    
    		<published>{ {$item.AddTimeFmt} }</published>
    
    		<updated>{ {$item.EditTimeFmt} }</updated>
    
    		<content type="text/plain">
    
    		  { {$item.Cname} } - { {$item.Name} } - { {$item.Des} }
    
    		</content>
    
        </entry>
    
    	{ {end} }
    
    </feed>
    
    `
    
    
    
    	db := h.App.Db
    
    
    
    	items := model.ArticleFeedList(db, 20, h.App.Cf.Site.TimeZone)
    
    
    
    	var upDate string
    
    	if len(items) > 0 {
    
    		upDate = items[0].AddTimeFmt
    
    	}
    
    
    
    	t := template.Must(template.New("feed").Parse(feed))
    
    	t.Execute(w, struct {
    
    		Update string
    
    		Items  []model.ArticleFeedListItem
    
    	}{
    
    		Update: upDate,
    
    		Items:  items,
    
    	})
    
    }
    
    
    
    func (h *BaseHandler) FeedCategoryHandler(w http.ResponseWriter, r *http.Request) {
    
    	w.Header().Set("Content-Type", "application/atom+xml; charset=utf-8")
    
    
    
    	scf := h.App.Cf.Site
    
    
    
    	var feed = `<?xml version="1.0" encoding="utf-8"?>
    
    <feed xmlns="http://www.w3.org/2005/Atom">
    
    	<title>` + scf.Name + `</title>
    
    	<link rel="self" type="application/atom+xml" href="` + scf.MainDomain + `/feed"/>
    
    	<link rel="hub" href="https://pubsubhubbub.appspot.com"/>
    
    	<updated>{ {.Update} }</updated>
    
    	<id>` + scf.MainDomain + `/feed</id>
    
    	<author>
    
    		<name>` + scf.Name + `</name>
    
    	</author>
    
    	{ {range $_, $item := .Items} }
    
    	<entry>
    
    		<title>{ {$item.Title} }</title>
    
    		<id>` + scf.MainDomain + `/t/{ {$item.Id} }</id>
    
    		<link rel="alternate" type="text/html" href="` + scf.MainDomain + `/t/{ {$item.Id} }" />
    
    		<published>{ {$item.AddTimeFmt} }</published>
    
    		<updated>{ {$item.EditTimeFmt} }</updated>
    
    		<content type="text/plain">
    
    		  { {$item.Cname} } - { {$item.Name} } - { {$item.Des} }
    
    		</content>
    
        </entry>
    
    	{ {end} }
    
    </feed>
    
    `
    
    
    
    	cid := pat.Param(r, "cid")
    
    	_, err := strconv.Atoi(cid)
    
    	if err != nil {
    
    		w.Write([]byte(`{"retcode":400,"retmsg":"cid type err"}`))
    
    		return
    
    	}
    
    			if cid == 19 || cid == 20 {
    
    		w.Write([]byte(`{"retcode":400,"retmsg":"cid is illeage"}`))
    
    		return
    
    					}
    
    	db := h.App.Db
    
    
    
    	items := model.ArticleFeedCategoryList(db, cid, 20, h.App.Cf.Site.TimeZone)
    
    
    
    	var upDate string
    
    	if len(items) > 0 {
    
    		upDate = items[0].AddTimeFmt
    
    	}
    
    
    
    	t := template.Must(template.New("feed").Parse(feed))
    
    	t.Execute(w, struct {
    
    		Update string
    
    		Items  []model.ArticleFeedListItem
    
    	}{
    
    		Update: upDate,
    
    		Items:  items,
    
    	})
    
    }
    
  32. tongtingwah   发表文章

    新冠病毒揭示了权力的本质

    本文翻译自2020年3月27日发表于《卫报》的“Coronavirus has not suspendedpolitics – it has revealed the nature of power”一文,文中所有观点仅代表作者个人意见。有删改。

    我们不断听到有人说,新冠肺炎疫情是一场战争。这是真的吗?使当前危机具有战时感觉的原因是缺乏正常的政治论点。首相在电视上发表严厉声明准备削减自由,反对派领导人对此却无任何意见,一味支持。议会仍在运作,但它似乎只是在通过首相提出的议案。人们被困在家里,他们的战斗仅限于家庭领域。人们开始谈论民族团结政府。“生活无处不政治”的观念已经被淡忘。

    但这不是政治的中止。揭开了政治生活的一层表皮,更能展现其背后更原始的东西。在民主国家,我们倾向于将政治视为各政党为争取我们的支持而进行的竞赛。我们关注政治生活的主体和内容,我们关心谁被投票选出来,当选者为我们提供了什么,谁从中受益。我们认为选举是解决这些问题的方式,但是在任何民主国家中,最大的问题始终是“如何实现”:政府将如何行使我们赋予他们的非凡权力?当他们这样做时,我们将如何回应?

    这些都是政治理论家一直关注的问题,但现在他们已经不只是理论了。正如当前的危机所表明的那样,政治存在的主要事实是有些人会告诉别人该怎么做。所有现代政治的核心是人身自由与集体选择之间的权衡,这是哲学家托马斯·霍布斯(Thomas Hobbes)在17世纪中叶发现的浮士德式交易,当时英国正因一场真正的内战而四分五裂。

    正如霍布斯所言,行使政治统治就是拥有对公民的生杀大权。我们可能赋予任何人权力的唯一原因是因为我们认为这是我们为集体安全付出的代价,但这也意味着我们将生死攸关的决定委托给我们最终无法控制的人。

    最主要的风险是,被选举出来的那些人拒绝执行民众的意愿。那时只有两种选择。要么人们被迫服从国家掌握的强制力,要么政治彻底崩溃,霍布斯认为后者是我们最应该担心的结果。

    我们可以奢侈地等待下一次选举来惩罚政治领导人的错误,但当基本生存问题受到威胁时,这样的“惩罚”只能是现实情况的安慰剂。无论如何,相对而言,这并不是什么惩罚。民众可能会失业,可能会丧命,但很少有政客会陷入贫困。

    这些政治原始性通常被寻求共识的民主要求所掩盖,但它们还没有消失。政府正在尽一切可能用常识性的建议来修饰其决定,宣称它仍然信任个人表现出正确的判断力。但是,正如其他欧洲国家的经验所示,随着危机的加深,严峻的现实变得更加清晰,只要看一下意大利市长尖叫着让他的选民们呆在家里的视频就足以体会这一点。“为谁投票”是民主政治的形式,“做得到与否”则是民主政治的原始本质。在这一点上,它看起来与任何其他类型的政治都没有太大不同。

    这场危机揭示了其他一些硬道理。国家政府确实很重要,而碰巧遇到哪个国家也很重要。尽管大流行是全球现象,并且许多不同地方经历了相似的情况,但该疾病造成的影响在很大程度上取决于各个政府的决策。何时采取行动?防疫措施以及能走多远?对于这些问题,各个国家都有不同的经验。最后,我们可能会看到谁是对的,谁是错的。但就目前而言,我们受到领导人的摆布。正如霍布斯警告过的,在所有政治的核心都不可避免地存在着独断的因素,这是个人政治判断的独断性。

    在封锁之下,民主国家揭示了它们与其他政治体制的共同点:这里的政治最终也与权力和秩序有关。但是,我们也开始看到一些基本差异,民主国家并不是更好、更友善、更温柔的地方。民主国家可能会尝试使这些美好愿望成为现实,但最终不会持久。民主国家确实很难做出真正艰难的抉择,在问题变得尖锐之前先发制人地解决问题,从来就不是民主的力量。我们总是等到别无选择,然后不得不适应现实,这意味着民主过程总是在疾病曲线爆发增长之后才开始,尽管有些国家的动作比其他国家更快。

    在战争中,敌人就站在面前。而在疾病大流行期间,病毒仅在每日感染和死亡病例的数据中昭示着它的存在。民主政体仿佛在黑暗中打拳,国家不知道谁是被传染的,谁是那个需要打败的“敌人”。

    一些民主国家设法更快地抵抗疫病流行。韩国通过广泛追踪和对可能的携带者进行监视来驯服这种疾病。但前提是,韩国在处理2015年Mers(中东呼吸综合征)爆发时已经积累了经验,当时的防疫措施影响了其公民的集体记忆,所以他们不会对现在政府的措施有太大怨言。以色列也许比许多欧洲国家做得更好,但是,以色列已经是一个长期处于战争状态的社会。当民众已经习惯时,适应会更容易。而像英国一样在进行过程中努力修补则要困难得多。

    近年来,有时似乎全球政治只是不同形式的技术官僚之间的竞赛。西方政府由经济学家和中央银行家统治,在民主制度的约束下运作。这给人造成一种印象,即运行庞大、复杂的经济和社会系统只需要技术判断。

    但是在过去的几周中,另一种现实浮出水面。政治运作最终需要的判断是关于如何使用强制力的,而不仅仅是技术问题,一定程度的独断权力是不可避免的。霍布斯在将近400年前试图逃脱的可怕又暴力的世界已经与我们相距甚远,但是我们的政治世界仍然没有超出霍布斯的认识范围。

    【作者】David Runciman 【作者简介】剑桥大学政治学教授

  33. 翻车了   发表文章

    【每日翻车新闻搬运】vol 283-284:电影解说竟敢影射今上!?

    https://www.reddit.com/r/hanren/comments/fs72ec/

    1 那位对线共青团的老哥原来也。。。


    2 支持一个,这种电影实在不多见了



    3 国师对线



    4 全部枪毙


    5 手冲左人


    6 那个假装美籍华人的家伙


    7 不看id的后果


    8 自裁 请



    9 内外有别


    10 儿子敢和爹提意见?忘了自己姓什么了?穆罕默德 立坚 赵?



    11 好名字


    12 b站敢死队


    13 一人就是一个n号房


    14 ??????


    15 历史资料


    vol 284:电影解说竟敢影射今上!?
    [https://www.reddit.com/r/hanren/comments/fs7sck/]( "https://www.reddit.com/r/hanren/comments/fs7sck/")

    1 爽,看你破产吃白饭更爽


    2 有意思。。。


    3 十几万。。。美国”户口"


    4 那位对线共青团老哥的视频。。。



    5 居然支持辱华品牌!汉奸!


    6 。。。。天啊


    7 知乎奇行种


    8 打倒马云!


    9 你说呢?


    10 那就尴尬了


    11 你敢影射今上?




    12 有事找大哥


    13 ??????????


    14 井底之蛙



    15 好熟悉啊


    16 谁想复辟啊?

    本次新闻播放完毕,谢谢欣赏!再次感谢原OP s1/refugee的汇总!

  34. 翻车了   发表文章

    【每日翻车新闻搬运】vol 286:土匪连压岁钱都不放过?

    https://www.reddit.com/r/hanren/comments/ftgd8s/

    1 纵轴坐标。。。


    2 生财有术



    3 生化危机3的918密码改了


    4 政治错误啊(笑)


    5 朝鲜有尊严/???那送你去移民啊


    6 怎么不抄作业了?


    7 赴美是生活啊



    8 压岁钱都不放过?


    9 辟谣X挂人√


    10 退款?


    11 反贼?韭菜?


    12 中 日 亲 善

    慰安妇都不许讨论了你还想着钓鱼岛呢?



    本次新闻播放完毕,谢谢欣赏!再次感谢原OP s1refugee的汇总!

  35. 翻车了   发表文章

    【每日翻车新闻搬运】vol 287-288:一切都是境外势力的错!& 真就上山下乡呗

    原地址:

    https://www.reddit.com/r/hanren/comments/ftgk22/

    https://www.reddit.com/r/hanren/comments/ftgnwt/

    vol 287

    1 牛夫人→小甜甜

    恶心,真他妈恶心

    2 动物之森早晚要完

    3 一年后再看如此讽刺

    4 禁用炎黄子孙 居然排在 领导人和台湾问题之前???

    5 求生欲极强

    6 批到批臭!(文革帽子来了)

    7 法制周刊反法制

    8 缺一不可?

    9 官僚主义

    10 知乎敢死队

    11 铁拳现世报

    12 好 律 师

    13 难道你们居然以为自己不会被当成韭菜?

    14 内外有别

    15 社会主义的事情,怎么能叫赌博呢(孔乙己体)

    16 一切都是境外势力的错!

    vol 288

    1 舔狗不得好死

    2 老马教徒了

    3 知乎敢死队





    4 上山下乡???

    5 这不就是一场过家家cosplay吗?

    6 微博敢死队

    7 比起加州大火还厉害啊。。。

    8 哦。。。。

    8 大预言家

    9 物理学。。。饭圈?????

    10 格萨尔(指正)

    本次新闻播放完毕,谢谢欣赏!再次感谢原OP s1/refugee的汇总!

  36. 笑翻江山   发表文章

    美国律师起诉中国各级政府隐瞒疫情的法律可行性

    滕彪认为,跟外国政府打官司在具体的操作过程中存在若干障碍,其中一个障碍是“送达”问题,就是将诉讼文书或法律文书送交被告方的行为。“送达”是美国法院的立案标准,就是说原告或者原告律师把法律文书送到法院之后,如果没有送达给被告方,或者被告方不愿意接收,就不能视同为通常所理解的立案。根据美国1976年的《外国政府的豁免法规定》,任何一个诉讼文书必须要送达到外交部长的手上,才视同为送达这个国家。

    滕彪教授提到的另外一个障碍就是国家主权豁免。通俗说就是国家行为不受外国法院管辖,主权豁免原则是全球通用的。他认为,中国被告可以用主权豁免原则拒绝应诉。

    在美国律师代理对中国政府发出集体诉讼之后一个星期,中国武汉律师梁旭光3月20日以“武汉公民”的名义向武汉中级法院提诉美国联邦政府、美国疾病控制与预防中心、美国国防部及美国军事体育理事会。控罪指“美军把病毒带到了武汉”,要求美国四“被告”赔偿其误工费,精神损害抚慰金,合计20万元人民币,并在媒体上向原告公开赔礼道歉等。

    中国司法观察网站3月25日刊发评论文章,题目是“武汉律师就COVID-19起诉美国政府?在中国,法律障碍可能浮出水面”。这里的法律障碍指的是,中国坚持绝对豁免权原则。根据这一立场,美国与其他任何国家一样,在中国享有主权豁免权。这意味着,在这种情况下,美国及其财产在中国法院享有绝对豁免权,包括绝对管辖权豁免权。即未经外国同意,对其提起的诉讼不予受理。

    评论还说,武汉法院不能根据自己的自由裁量权受理此案,而应征求最高人民法院(SPC)的指示。

    国家豁免权亦称国家主权豁免,由于它往往是以国家财产的豁免问题被提出来,所以通常又称之为国家及其财产豁免。它是指在国际交往中,一个国家及其财产未经其明示同意免受其他国家的司法管辖和执行。

    旅美学者、中国人权律师陈建刚以他个人的经历谈中国公民个人起诉外国政府。他认为,梁旭光起诉美国政府等几个被告,“就是一场由政治势力在背后导演的闹剧。因为,现时中你在中国连一个乡政府都不能起诉,反而起诉美国政府,

    这显然是龌龊的闹剧”。

    https://www.voachinese.com/a/the-legal-feasibility-of-american-lawyers-class-action-against-six-defendants-in-china-20200331/5353412.html

  37. natasha 饭姐
    natasha   回复文章

    tt

    @origin #7

    我不是专门学哲学的,也是外行,随便说一下: 你说的意识形态对立,其实是从马克思主义社会哲学的角度出发的。sean转发的这篇文章中哲学家门派各异:齐泽克是精神分析学派的;巴特勒是研究Gender的;阿甘本也是研究社会哲学的,他和韩炳哲都收到了海德格尔的影响,即德国文化批判哲学和诠释学那一个派别。

  38. sean   回复文章

    tt

    @origin #7 是,你可以进而搜索阿甘本最近的其他文章。

  39. natasha 饭姐
    natasha   回复文章

    tt

    @sean #6

    好文章,谢谢分享。

  40. origin   回复文章

    tt

    @sean #6 除了齐泽克和福柯,我大概就不太认识这篇文章当中提到的哲学家 文章中并没有非常明确地说明阿甘本的例外理论,你原因这篇文章是想说明西方意识形态的对立吗?

  41. sean   回复文章

    tt

    @origin #5 https://www.dushu.com/news/13069/

  42. origin   回复文章

    tt

    @natasha #4 这个问题在几个方面已经不是公共卫生积累经验的问题了,普通民众是否需要全体佩戴口罩可能存在争议,但是医护人员在任何时候都应该配戴口罩,更何况是诊治患有传染性疾病的患者。私以为欧美公众将带不带口罩问题政治化,将其变成了意识形态的对立

  43. PumpkinAvatar   发表文章

    恁佬屏蔽俺发言,这种做法对恁佬有益吗

    无异于掩耳盗铃

    此等碰瓷手法实在太恶劣了

    抗议 @小二

  44. natasha 饭姐
    natasha   回复文章

    tt

    @kyq #3

    我不懂公共卫生哈,只是一点自己的看法:公共卫生和纯医疗科学有所区别,公共卫生很大程度上是靠实践积累经验。亚洲的公共卫生专家和医生(不是政府官员)有了sars的经验,也多亏这次疫情在亚洲发生得早,因此亚洲医疗人员在经验上比欧洲丰富得多。

  45. rebecca   回复文章

    上诉:ereee账户被无理封禁

    连我都没有说梅菲斯特是sb,你有什么资格说

  46. origin   回复文章

    资本主义大脑+社会主义躯干

    至少迄今为止,这条道路不能被认为是错误的选择,甚至如果你认同历史假设论的话,经济发展优先策略可能是相对优解。苏联不是共产主义,其内部对于经济体制的定义是国家资本主义,因为根据马克思的理论,社会主义的是资本主义发展的结果,这也是为何在二战时期,苏联优先扶持的是国民党而非中共。至少当前资本主义发展的内生矛盾,在国内还基本属于可控制的范围,我认为这些矛盾的控制很大程度上得益于社会主义再分配机制,只能说中国政府在合适的时间段,做了一件不算太错的事情。