cat /var/lib/tor/hidden_service/hostname
执行上面这个命令,小二应该就可以看到2049bbs.xyz的onion域名了
cat /var/lib/tor/hidden_service/hostname
执行上面这个命令,小二应该就可以看到2049bbs.xyz的onion域名了
@小二 #3 不知道,点了“发表”,什么反应都没有。。
算了,有空把那些代码都改了吧。我是手机,没法git clone完整的项目,所以我都是怎么改简单,怎么来的
树洞、水区和垃圾场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)
}
@翻车了 #1 我发不了帖子了!!!
我觉得,feed那段代码,实在不敢恭维!
feed下,走的是 ArticleFeedList
feed节点下,走的是 ArticleFeedCategoryList
我也想在ArticleFeedList里写,但是我觉得很多代码完全是重复而且冗余的
如果我来改,我是从route层就已经改掉了,后面的代码会精简很多
@小二 #94 很多issue没明白你想做什么 关于@相关功能,我可以 https://github.com/Terminus2049/2049bbs/blob/master/controller/article.go 关于rss相关功能,我可以 https://github.com/Terminus2049/2049bbs/blob/master/controller/feed.go
写好了我发出来
如果小二有什么想实现的功能,我能帮忙实现的,我都会写好放到2049,你可以自己看看接受与否。GitHub上就不提pr了
@张三 #84 正常成年人都需要上班,不可能熊熊那样24小时水帖。如果你看熊熊的发帖时间和规律就能知道,绝对不可能在上班时间。她是不是女生不知道。但肯定是因为瘟疫,没上学的学生
@小二 #91 你说的1700威望那个人,反共反魔怔了。他经常发一些悲剧,不去同情受害人,却幸灾乐祸,觉得又是乳化,乳包的好材料。它就是一个把自己快乐建立在别人的痛苦之上的人。
这次熊熊被欺负,不是第一时间去关心熊熊身心健康。反而处处刁难Rebecca,然后泼2049脏水。
这个人灵魂都已经扭曲了。
@Phragmites #78 小二的意思,你不要把事情弄得太严肃
@Phragmites #83 大家都对这个事情很严肃,我已经帮忙写了代码给小二参考。说的不是熊熊的事情,是你。所以你没有明白
@小二 #4 不知道能帮到多少忙
https://zhuanlan.zhihu.com/p/47681576
https://zhuanlan.zhihu.com/p/47682212
这里没法排版,看这里好了
https://nbviewer.jupyter.org/github/quantumlib/OpenFermion/blob/master/examples/jordan_wigner_and_bravyi_kitaev_transforms.ipynb
狄拉克符号 打不出来!!!! 全乱码了!!!
神奇了,矩阵伪逆符号居然显示是正确的,这个markdown编辑器功能真的是缩水啊
Molecular Hamiltonians are known to have certain symmetries that are not taken into account by mappings like the Jordan-Wigner or Bravyi-Kitaev transform. The most notable of such symmetries is the conservation of the total number of particles in the system. Since those symmetries effectively reduce the degrees of freedom of the system, one is able to reduce the number of qubits required for simulation by utilizing binary codes (arXiv:1712.07067).
We can represent the symmetry-reduced Fermion basis by binary vectors of a set $\mathcal{V} \ni \boldsymbol{\nu}$, with $ \boldsymbol{\nu} = (\nu_0, \, \nu_1, \dots, \, \nu_{N-1} ) $, where every component $\nu_i \in \lbrace 0, 1 \rbrace $ and $N$ is the total number of Fermion modes. These binary vectors $ \boldsymbol{\nu}$ are related to the actual basis states by: $$ \left[\prod_{i=0}^{N-1} (a_i^{\dagger})^{\nu_i} \right] \left|{\text{vac}}\right\rangle \, , $$ where $ (a_i^\dagger)^{0}=1$. The qubit basis, on the other hand, can be characterized by length-$n$ binary vectors $\boldsymbol{\omega}=(\omega_0, \, \dots , \, \omega_{n-1})$, that represent an $n$-qubit basis state by: $$ \left|{\omega_0}\right\rangle \otimes \left|\omega_1\right\rangle \otimes \dots \otimes \left|{\omega_{n-1}}\right\rangle \, . $$ Since $\mathcal{V}$ is a mere subset of the $N$-fold binary space, but the set of the vectors $\boldsymbol{\omega}$ spans the entire $n$-fold binary space we can assign every vector $\boldsymbol{\nu}$ to a vector $ \boldsymbol{\omega}$, such that $n<N$. This reduces the amount of qubits required by $(N-n)$. The mapping can be done by a binary code, a classical object that consists of an encoder function $\boldsymbol{e}$ and a decoder function $\boldsymbol{d}$. These functions relate the binary vectors $\boldsymbol{e}(\boldsymbol{\nu})=\boldsymbol{\omega}$, $\boldsymbol{d}(\boldsymbol{\omega})=\boldsymbol{\nu}$, such that $\boldsymbol{d}(\boldsymbol{e}(\boldsymbol{\nu}))=\boldsymbol{\nu}$. In OpenFermion, we, at the moment, allow for non-linear decoders $\boldsymbol{d}$ and linear encoders $\boldsymbol{e}(\boldsymbol{\nu})=A \boldsymbol{\nu}$, where the matrix multiplication with the $(n\times N)$-binary matrix $A$ is $(\text{mod 2})$ in every component.
The non-linear binary functions for the components of the decoder are here modeled by the $\text{BinaryPolynomial}$ class in openfermion.ops. For initialization we can conveniently use strings ('w0 w1 + w1 +1' for the binary function $\boldsymbol{\omega} \to \omega_0 \omega_1 + \omega_1 + 1 \;\text{mod 2}$), the native data structure or symbolic addition and multiplication.
高通滤波和低通滤波明天记录下
安全可靠,小二可以自己比对下我的修改 @小二 如果有任何问题,都可以@我
修改代码 https://github.com/Terminus2049/2049bbs/blob/df0914d35b4be3ccf6ae1e8da525b355dd54bd01/controller/user.go
登录的时候,访问 UserLogin 函数 修改如下
func (h *BaseHandler) UserLogin(w http.ResponseWriter, r *http.Request) {
type pageData struct {
PageData
Act string
Token string
CaptchaId string
}
act := strings.TrimLeft(r.RequestURI, "/")
title := "登录"
if act == "register" {
title = "注册"
}
tpl := h.CurrentTpl(r)
evn := &pageData{}
evn.SiteCf = h.App.Cf.Site
evn.Title = title
evn.Keywords = ""
evn.Description = ""
evn.IsMobile = tpl == "mobile"
evn.ShowSideAd = true
evn.PageName = "user_login_register"
evn.Act = act
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", "userlogin.html")
}
提交代码修改如下
func (h *BaseHandler) UserLoginPost(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
}
act := strings.TrimLeft(r.RequestURI, "/")
type recForm struct {
Name string `json:"name"`
Password string `json:"password"`
Response string `json:"g-recaptcha-response"`
}
type response struct {
normalRsp
}
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()
if len(rec.Name) == 0 || len(rec.Password) == 0 {
w.Write([]byte(`{"retcode":400,"retmsg":"name or pw is empty"}`))
return
}
nameLow := strings.ToLower(rec.Name)
if !util.IsUserName(nameLow) {
w.Write([]byte(`{"retcode":400,"retmsg":"name fmt err"}`))
return
}
// did we get a proper recaptcha response? if null, redirect back to sigup page
if rec.Response == "" {
// user press submit button without passing reCAPTCHA test
// abort
http.Redirect(w, r, "/", 301)
return // return control to stop execution, otherwise it will continue
}
// get end user's IP address
remoteip, _, _ := net.SplitHostPort(r.RemoteAddr)
fmt.Println("remote ip : ", remoteip)
// to verify if the recaptcha is REAL. we must send
// secret + response + remoteip(optional) to postURL
secret := "[你的 secret key]"
postURL := "https://www.google.com/recaptcha/api/siteverify"
postStr := url.Values{"secret": {secret}, "response": {rec.Response}, "remoteip": {remoteip}}
responsePost, err := http.PostForm(postURL, postStr)
if err != nil {
fmt.Println(err)
return
}
defer responsePost.Body.Close()
body, err := ioutil.ReadAll(responsePost.Body)
if err != nil {
fmt.Println(err)
return
}
// this part is for server side verification
var APIResp JSONAPIResponse
json.Unmarshal(body, &APIResp)
fmt.Println(APIResp)
db := h.App.Db
timeStamp := uint64(time.Now().UTC().Unix())
if act == "login" {
bn := "user_login_token"
key := []byte(token + ":loginerr")
if db.Zget(bn, key).State == "ok" {
// todo
//w.Write([]byte(`{"retcode":400,"retmsg":"name and pw not match"}`))
//return
}
uobj, err := model.UserGetByName(db, nameLow)
if err != nil {
w.Write([]byte(`{"retcode":405,"retmsg":"json Decode err:` + err.Error() + `","newCaptchaId":"` + captcha.New() + `"}`))
return
}
if uobj.Password != rec.Password {
db.Zset(bn, key, uint64(time.Now().UTC().Unix()))
w.Write([]byte(`{"retcode":405,"retmsg":"name and pw not match","newCaptchaId":"` + captcha.New() + `"}`))
return
}
sessionid := xid.New().String()
uobj.LastLoginTime = timeStamp
uobj.Session = sessionid
jb, _ := json.Marshal(uobj)
db.Hset("user", youdb.I2b(uobj.Id), jb)
h.SetCookie(w, "SessionID", strconv.FormatUint(uobj.Id, 10)+":"+sessionid, 365)
} else {
// register
siteCf := h.App.Cf.Site
if siteCf.CloseReg {
w.Write([]byte(`{"retcode":400,"retmsg":"stop to new register"}`))
return
}
if db.Hget("user_name2uid", []byte(nameLow)).State == "ok" {
w.Write([]byte(`{"retcode":405,"retmsg":"name is exist","newCaptchaId":"` + captcha.New() + `"}`))
return
}
userId, _ := db.HnextSequence("user")
flag := 5
if siteCf.RegReview {
flag = 1
}
if userId == 1 {
flag = 99
}
uobj := model.User{
Id: userId,
Name: rec.Name,
Password: rec.Password,
Flag: flag,
RegTime: timeStamp,
LastLoginTime: timeStamp,
Session: xid.New().String(),
}
// 从指定的用户中随机选一个头像作为新注册用户头像
// 指定用户必须连续,取最小id和最大id
rand.Seed(time.Now().UnixNano())
min := siteCf.AvatarMinId // 2539
max := siteCf.AvatarMaxId // 2558
sampleID := rand.Intn(max-min+1) + min
uidStr := strconv.FormatUint(uint64(sampleID), 10)
uobj.Avatar = uidStr
uobj.IgnoreLimitedUsers = true
jb, _ := json.Marshal(uobj)
db.Hset("user", youdb.I2b(uobj.Id), jb)
db.Hset("user_name2uid", []byte(nameLow), youdb.I2b(userId))
db.Hset("user_flag:"+strconv.Itoa(flag), youdb.I2b(uobj.Id), []byte(""))
h.SetCookie(w, "SessionID", strconv.FormatUint(uobj.Id, 10)+":"+uobj.Session, 365)
}
h.DelCookie(w, "token")
rsp := response{}
rsp.Retcode = 200
json.NewEncoder(w).Encode(rsp)
}
额,忘了几个地方
前端html 增加
<script src='https://www.google.com/recaptcha/api.js'></script>
后端golang部分
type JSONAPIResponse struct {
Success bool `json:"success"`
ChallengeTS time.Time `json:"challenge_ts"` // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
Hostname string `json:"hostname"` // the hostname of the site where the reCAPTCHA was solved
ErrorCodes []int `json:"error-codes"` //optional
}
增加
json.Unmarshal(body, &APIResp)
fmt.Println(APIResp)
增加判断APIResp.Success 是true还是false 的代码就可以了
@Shiro #55 标准HTTP就不带Mac地址,而且这个不是唯一可以伪造。还违背了匿名承诺
@hello _chris #52 https://www.socketloop.com/tutorials/golang-recaptcha-example
我觉得这个说的很清楚,晚上我改个demo发出来,我觉得小二自己可以搞定的
不觉得敏感词就一定是政治审核。隔壁有些人,他们就是来树洞参观的,从不发帖子,你让他们注册写什么,自我介绍么?
如果小二觉得人少,人肉审核可以接受,那我觉得对注册方而言,要等很久才可能知道自己注册成功了,可能就没意思了,谁注册不是一时兴起呢?
我觉得品葱管理员不应该干涉2049内政,如果参与讨论应该以2049用户身份。
应该使用敏感词系统,YouTube也有黄标,这个是正常网站运营的正常手段。可以根据各个时期增加删除。不应该因为共产党用了这个技术就排斥这个技术。
共产党控制下也有医院,难道医院就是邪恶的么?
技术不存在正邪之分,完全取决于怎么用和为什么用?
小号属于合法行使自己的言论自由,不应该因为是小号被禁,而应该取决于言论是不是符合2049的理性讨论注册申明。
@陈士杰 #16 我在11楼表达了我的理解,管理国家的是一个团体。这个团体不一定叫党。比如朱元璋成立明朝。他没有管自己那个团体叫党,但他代表了那个利益集团。我觉得只是现代化社会,人类社团组织形式得到了进化,党成为了现代化政治的标准
Google搜中文都已经是垃圾了,应该说中文已经被污染了。不止网文
@BE4 #5 好吧
@Hker #22 前辈保重~
@rebecca #19 我说了啊,大学才学的编程。工商管理也不好找工作的,化学也没有好到哪里去。我在品葱被封了,才跑这里的,不会回品葱的了
@rebecca #19
https://2049bbs.xyz/t/3845
前辈可以看看这个帖子的20楼,merlin前辈读phd,也没用过我说的那两个软件
我觉得应该毕业无望了,,,
@rebecca #16 我上大学才开始学编程。但是我在高一就注册了v2ex。所以我已经在v2ex混了7年了~
我学的工商管理,很简单的,反而她的化学我觉得难的可怕,我高数学的不差,微积分也很好,但是她课本里的数学公式和符号都是我不曾见过的!其实她成绩很好的,只是编程没有怎么实战。她让我帮忙写写代码而已。
我专门搜了psi4和pyscf的api文档和demo来看,果然不是web的crud级别的,根本看不懂!
她估计会跟我回老家找工作,不是她的问题,而是化学专业找工作不容易罢了!
恐怖分子,它是一个个自己人工注册的,不是写脚本批量注册。现在通过限制注册的方式,对恐怖分子无效,缘木求鱼!
更多应该是识别出这个账号是恐怖分子!最简单的办法,就是敏感词。
个人认为,像“肉棒”这个词汇,明显不是合理词汇,有这类词汇就自动转垃圾区,就可以了。
@rebecca #13 不不不,化学那块,不论是你还是我,都不可能半个星期搞定。他们的化学课不是高中那种过家家。难得我觉得很恐怖的。最起码,我不知道化学里的共轭是个什么鬼!这一切化学的东西都是她自己搞定,我们只要敲代码实现就可以了。她导师给的估计是大概要4/5千行就可以了。我们应该可以搞定的。
我和她没钱,我们两个自己搞定吧,不找你了
@rebecca #11 别和他理论了,他不去安慰熊熊,处处针对你,把脏水都泼你身上,有机会就排挤你。
现在最重要的是保护好熊熊,安抚她受伤的心,而不是去借题发挥攻击其他人。
他连这点情商都没有。你和他浪费感情干嘛
大神,我觉得限制注册的手段没用!因为这次捣乱分子是活人注册。如果你能拦住它注册,也就是拦住了普通人类注册。
还是和隔壁品葱一样,增加敏感词吧。发出未成年不宜的词汇,就自动发到垃圾区。这个开发量,对你来说最小,也最简单。
@rebecca #9 我是工商管理,她是化学。她答辩,我负责写代码帮她。但是这东西我也不会,她学的化学难的要死,我反正看不懂
你可以自己改代码,开源的
@rebecca #4 你放心好了,这里不是品葱。共产党没有盯上这里。我也从来没有说过反动的话,国安顶不上我们的
rbc前辈,你现在回品葱又能如何?不如留下来?
晚辈下周就要入职上班了,我还能陪你半个星期!
晚辈希望,这半个星期,我们两个可以完成《聚乙烯吡咯烷酮 与含-COOH官能团的分子间π interaction的能量成分分析》论文,基于Psi4 或者 PySCF开发!这个关系到晚辈的女友能不能本科毕业!我希望她顺利毕业后我们两个人结婚~
算了,你在品葱已经混不下去了。留下来陪我好了
@饱读书名 #10 你说的对